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

NEW API: GetObjectAttributes #1921

Merged
merged 14 commits into from
Jan 8, 2024
133 changes: 133 additions & 0 deletions api-get-object-attributes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package minio
zveinn marked this conversation as resolved.
Show resolved Hide resolved

import (
"context"
"encoding/xml"
"net/http"
"net/url"
"strconv"
"time"

"github.com/minio/minio-go/v7/pkg/encrypt"
"github.com/minio/minio-go/v7/pkg/s3utils"
)

// ObjectAttributesOptions is an API call that combines
// HeadObject and ListParts.
//
// VersionID - The object version you want to attributes for
// ServerSideEncryption - The server-side encryption algorithm used when storing this object in Minio
type ObjectAttributesOptions struct {
VersionID string
ServerSideEncryption encrypt.ServerSide
}

// ObjectAttributes ...
type ObjectAttributes struct {
ObjectAttributesResponse
LastModified time.Time
VersionID string
}

func (o *ObjectAttributes) parseResponse(resp *http.Response) (err error) {
mod, err := parseRFC7231Time(resp.Header.Get("Last-Modified"))
if err != nil {
return err
}
o.LastModified = mod
o.VersionID = resp.Header.Get(amzVersionID)

response := new(ObjectAttributesResponse)
if err := xml.NewDecoder(resp.Body).Decode(response); err != nil {
return err
}
o.ObjectAttributesResponse = *response

return
}

// ObjectAttributesResponse ...
zveinn marked this conversation as resolved.
Show resolved Hide resolved
type ObjectAttributesResponse struct {
ETag string `xml:",omitempty"`
StorageClass string
ObjectSize int
Checksum struct {
ChecksumCRC32 string `xml:",omitempty"`
ChecksumCRC32C string `xml:",omitempty"`
ChecksumSHA1 string `xml:",omitempty"`
ChecksumSHA256 string `xml:",omitempty"`
}
ObjectParts struct {
PartsCount int
Parts []*ObjectAttributePart `xml:"Part"`
}
}

// ObjectAttributePart ...
zveinn marked this conversation as resolved.
Show resolved Hide resolved
type ObjectAttributePart struct {
ChecksumCRC32 string `xml:",omitempty"`
ChecksumCRC32C string `xml:",omitempty"`
ChecksumSHA1 string `xml:",omitempty"`
ChecksumSHA256 string `xml:",omitempty"`
PartNumber int
Size int
}

// GetObjectAttributes ...
zveinn marked this conversation as resolved.
Show resolved Hide resolved
// This API combines HeadObject and ListParts.
func (c *Client) GetObjectAttributes(ctx context.Context, bucketName, objectName string, opts ObjectAttributesOptions) (ObjectAttributes, error) {
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return ObjectAttributes{}, err
}

if err := s3utils.CheckValidObjectName(objectName); err != nil {
return ObjectAttributes{}, err
}

urlValues := make(url.Values)
urlValues.Add("attributes", "")
if opts.VersionID != "" {
urlValues.Add("versionId", opts.VersionID)
}

headers := make(http.Header)
headers.Set(amzObjectAttributes, GetObjectAttributesTags)

// Setting maxPartsCount here will ensure we always get
// all objecrt parts back. AWS S3 limits each request to
// 1000 parts unless told to retrieve more.
headers.Set(amzMaxParts, strconv.Itoa(maxPartsCount))

if opts.ServerSideEncryption != nil {
opts.ServerSideEncryption.Marshal(headers)
}

resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{
bucketName: bucketName,
objectName: objectName,
queryValues: urlValues,
contentSHA256Hex: emptySHA256Hex,
customHeader: headers,
})
if err != nil {
return ObjectAttributes{}, err
}
defer closeResponse(resp)

if resp.StatusCode != http.StatusOK {
ER := new(ErrorResponse)
if err := xml.NewDecoder(resp.Body).Decode(ER); err != nil {
return ObjectAttributes{}, err
}

return ObjectAttributes{}, *ER
}

OA := new(ObjectAttributes)
err = OA.parseResponse(resp)
if err != nil {
return ObjectAttributes{}, err
}

return *OA, nil
}
13 changes: 13 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,25 @@ const (
)

const (
// GetObjectAttributesTags are tags used to defined
// return values for the GetObjectAttributes API
GetObjectAttributesTags = "ETag,Checksum,StorageClass,ObjectSize,ObjectParts"
)

const (

// Storage class header.
amzStorageClass = "X-Amz-Storage-Class"

// Website redirect location header
amzWebsiteRedirectLocation = "X-Amz-Website-Redirect-Location"

// GetObjectAttributes headers
amzPartNumberMarker = "X-Amz-Part-Number-Marker"
amzExpectedBucketOnwer = "X-Amz-Expected-Bucket-Owner"
amzMaxParts = "X-Amz-Max-Parts"
amzObjectAttributes = "X-Amz-Object-Attributes"

// Object Tagging headers
amzTaggingHeader = "X-Amz-Tagging"
amzTaggingHeaderDirective = "X-Amz-Tagging-Directive"
Expand Down