Skip to content

Commit

Permalink
NEW API: GetObjectAttributes (#1921)
Browse files Browse the repository at this point in the history
  • Loading branch information
zveinn authored Jan 8, 2024
1 parent 56d9949 commit 76a4146
Show file tree
Hide file tree
Showing 5 changed files with 1,230 additions and 281 deletions.
201 changes: 201 additions & 0 deletions api-get-object-attributes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
* MinIO Go Library for Amazon S3 Compatible Cloud Storage
* Copyright 2020 MinIO, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package minio

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 are options used for the GetObjectAttributes API
//
// - MaxParts
// How many parts the caller wants to be returned (default: 1000)
//
// - VersionID
// The object version you want to attributes for
//
// - PartNumberMarker
// the listing will start AFTER the part matching PartNumberMarker
//
// - ServerSideEncryption
// The server-side encryption algorithm used when storing this object in Minio
type ObjectAttributesOptions struct {
MaxParts int
VersionID string
PartNumberMarker int
ServerSideEncryption encrypt.ServerSide
}

// ObjectAttributes is the response object returned by the GetObjectAttributes API
//
// - VersionID
// The object version
//
// - LastModified
// The last time the object was modified
//
// - ObjectAttributesResponse
// Contains more information about the object
type ObjectAttributes struct {
VersionID string
LastModified time.Time
ObjectAttributesResponse
}

// ObjectAttributesResponse contains details returned by the GetObjectAttributes API
//
// Noteworthy fields:
//
// - ObjectParts.PartsCount
// Contains the total part count for the object (not the current response)
//
// - ObjectParts.PartNumberMarker
// Pagination of parts will begin at (but not include) PartNumberMarker
//
// - ObjectParts.NextPartNumberMarket
// The next PartNumberMarker to be used in order to continue pagination
//
// - ObjectParts.IsTruncated
// Indicates if the last part is included in the request (does not check if parts are missing from the start of the list, ONLY the end)
//
// - ObjectParts.MaxParts
// Reflects the MaxParts used by the caller or the default MaxParts value of the API
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
PartNumberMarker int
NextPartNumberMarker int
MaxParts int
IsTruncated bool
Parts []*ObjectAttributePart `xml:"Part"`
}
}

// ObjectAttributePart is used by ObjectAttributesResponse to describe an object part
type ObjectAttributePart struct {
ChecksumCRC32 string `xml:",omitempty"`
ChecksumCRC32C string `xml:",omitempty"`
ChecksumSHA1 string `xml:",omitempty"`
ChecksumSHA256 string `xml:",omitempty"`
PartNumber int
Size int
}

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
}

// GetObjectAttributes API combines HeadObject and ListParts.
// More details on usage can be found in the documentation for ObjectAttributesOptions{}
func (c *Client) GetObjectAttributes(ctx context.Context, bucketName, objectName string, opts ObjectAttributesOptions) (*ObjectAttributes, error) {
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return nil, err
}

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

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

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

if opts.PartNumberMarker > 0 {
headers.Set(amzPartNumberMarker, strconv.Itoa(opts.PartNumberMarker))
}

if opts.MaxParts > 0 {
headers.Set(amzMaxParts, strconv.Itoa(opts.MaxParts))
} else {
headers.Set(amzMaxParts, strconv.Itoa(GetObjectAttributesMaxParts))
}

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 nil, err
}

defer closeResponse(resp)

hasEtag := resp.Header.Get(ETag)
if hasEtag != "" {
return nil, errors.New("getObjectAttributes is not supported by the current endpoint version")
}

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

return nil, *ER
}

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

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

const (
// GetObjectAttributesTags are tags used to defined
// return values for the GetObjectAttributes API
GetObjectAttributesTags = "ETag,Checksum,StorageClass,ObjectSize,ObjectParts"
// GetObjectAttributesMaxParts defined the default maximum
// number of parts returned by GetObjectAttributes
GetObjectAttributesMaxParts = 1000
)

const (
// Response Headers

// ETag is a common response header
ETag = "ETag"

// 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
56 changes: 55 additions & 1 deletion docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func main() {
| | [`GetObjectTagging`](#GetObjectTagging) | | | |
| | [`RemoveObjectTagging`](#RemoveObjectTagging) | | | |
| | [`RestoreObject`](#RestoreObject) | | | |
| | [`GetObjectAttributes`](#GetObjectAttributes) | | | |

## 1. Constructor
<a name="MinIO"></a>
Expand Down Expand Up @@ -445,8 +446,8 @@ __minio.GetObjectOptions__
|:---|:---|:---|
| `opts.ServerSideEncryption` | _encrypt.ServerSide_ | Interface provided by `encrypt` package to specify server-side-encryption. (For more information see https://godoc.org/github.com/minio/minio-go/v7) |
| `opts.Internal` | _minio.AdvancedGetOptions_ | This option is intended for internal use by MinIO server. This option should not be set unless the application is aware of intended use.
__Return Value__

__Return Value__

|Param |Type |Description |
|:---|:---| :---|
Expand Down Expand Up @@ -1194,6 +1195,59 @@ if err != nil {
}
```

<a name="GetObjectAttributes"></a>
### GetObjectAttributes(ctx context.Context, bucketName, objectName string, opts ObjectAttributesOptions) (*ObjectAttributes, error)
Returns a stream of the object data. Most of the common errors occur when reading the stream.


__Parameters__


|Param |Type |Description |
|:---|:---| :---|
|`ctx` | _context.Context_ | Custom context for timeout/cancellation of the call|
|`bucketName` | _string_ |Name of the bucket |
|`objectName` | _string_ |Name of the object |
|`opts` | _minio.ObjectAttributesOptions_ | Configuration for pagination and selection of object attributes |


__minio.ObjectAttributesOptions__

|Field | Type | Description |
|:---|:---|:---|
| `opts.ServerSideEncryption` | _encrypt.ServerSide_ | Interface provided by `encrypt` package to specify server-side-encryption. (For more information see https://godoc.org/github.com/minio/minio-go/v7) |
| `opts.MaxParts` | _int | This option defines how many parts should be returned by the API
| `opts.VersionID` | _string | VersionID defines which version of the object will be used
| `opts.PartNumberMarker` | _int | This options defines which part number pagination will start after, the part which number is equal to PartNumberMarker will not be included in the response

__Return Value__

|Param |Type |Description |
|:---|:---| :---|
|`objectAttributes` | _*minio.ObjectAttributes_ |_minio.ObjectAttributes_ contains the information about the object and it's parts. |

__Example__


```go
objectAttributes, err := c.GetObjectAttributes(
context.Background(),
"your-bucket",
"your-object",
minio.ObjectAttributesOptions{
VersionID:"object-version-id",
NextPartMarker:0,
MaxParts:100,
})

if err != nil {
fmt.Println(err)
return
}

fmt.Println(objectAttributes)
```


<a name="RemoveIncompleteUpload"></a>
### RemoveIncompleteUpload(ctx context.Context, bucketName, objectName string) error
Expand Down
Loading

0 comments on commit 76a4146

Please sign in to comment.