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
155 changes: 155 additions & 0 deletions api-get-object-attributes.go
@@ -0,0 +1,155 @@
package minio
zveinn marked this conversation as resolved.
Show resolved Hide resolved

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

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

// ObjectAttributesOptions ...
zveinn marked this conversation as resolved.
Show resolved Hide resolved
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAttributes.html
type ObjectAttributesOptions struct {
ObjectAttributes string
zveinn marked this conversation as resolved.
Show resolved Hide resolved
VersionID string
MaxParts int
PartNumberMarker int
zveinn marked this conversation as resolved.
Show resolved Hide resolved
ServerSideEncryption encrypt.ServerSide

// Bucket onwer is an S3 specific parameter
BucketOwner string
}

// ObjectAttributes ...
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAttributes.html
type ObjectAttributes struct {
ObjectAttributesResponse
LastModified time.Time
VersionID string
RequestCharged string
}

// ParseResponse ...
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAttributes.html
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
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAttributes.html
type ObjectAttributesResponse struct {
ETag string `xml:",omitempty"`
Checksum struct {
ChecksumCRC32 string `xml:",omitempty"`
ChecksumCRC32C string `xml:",omitempty"`
ChecksumSHA1 string `xml:",omitempty"`
ChecksumSHA256 string `xml:",omitempty"`
}
ObjectParts struct {
PartsCount int
PartNumberMarker int
NextPartNumberMarker int
MaxParts int
IsTruncated bool
zveinn marked this conversation as resolved.
Show resolved Hide resolved
Parts []*struct {
zveinn marked this conversation as resolved.
Show resolved Hide resolved
ChecksumCRC32 string `xml:",omitempty"`
ChecksumCRC32C string `xml:",omitempty"`
ChecksumSHA1 string `xml:",omitempty"`
ChecksumSHA256 string `xml:",omitempty"`
PartNumber int
Size int
} `xml:"Part"`
}
StorageClass string
ObjectSize int
}

// GetObjectAttributes verifies if object exists, you have permission to access it
// and returns information about the object.
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAttributes.html
func (c *Client) GetObjectAttributes(ctx context.Context, bucketName, objectName string, opts ObjectAttributesOptions) (ObjectAttributes, error) {
// Input validation.

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, opts.ObjectAttributes)

if len(opts.ObjectAttributes) < 1 {
return ObjectAttributes{}, errors.New("object attribute tags are required")
}

headers.Set(amzPartNumberMarker, strconv.Itoa(opts.PartNumberMarker))

if opts.BucketOwner != "" {
headers.Set(amzExpectedBucketOnwer, opts.BucketOwner)
}

if opts.MaxParts != 0 {
headers.Set(amzMaxParts, strconv.Itoa(opts.MaxParts))
}

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
}

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

return ObjectAttributes{}, *ER
}

defer closeResponse(resp)
zveinn marked this conversation as resolved.
Show resolved Hide resolved

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

return *OA, nil
}
6 changes: 6 additions & 0 deletions constants.go
Expand Up @@ -66,6 +66,12 @@ const (
// 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