[#196] Add GetObjectTagging

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2021-08-17 14:23:01 +03:00 committed by Alex Vanin
parent 8b5ebe2ec2
commit 4a2575fbf3
7 changed files with 92 additions and 8 deletions

View file

@ -65,7 +65,7 @@ func fetchRangeHeader(headers http.Header, fullSize uint64) (*layer.RangeParams,
return &layer.RangeParams{Start: start, End: end}, nil
}
func writeHeaders(h http.Header, info *layer.ObjectInfo) {
func writeHeaders(h http.Header, info *layer.ObjectInfo, tagSetLength int) {
if len(info.ContentType) > 0 {
h.Set(api.ContentType, info.ContentType)
}
@ -73,6 +73,7 @@ func writeHeaders(h http.Header, info *layer.ObjectInfo) {
h.Set(api.ContentLength, strconv.FormatInt(info.Size, 10))
h.Set(api.ETag, info.HashSum)
h.Set(api.AmzVersionID, info.ID().String())
h.Set(api.AmzTaggingCount, strconv.Itoa(tagSetLength))
for key, val := range info.Headers {
h[api.MetadataPrefix+key] = []string{val}
@ -119,7 +120,14 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
h.logAndSendError(w, "could not parse range header", reqInfo, err)
return
}
writeHeaders(w.Header(), info)
tagSet, err := h.obj.GetObjectTagging(r.Context(), info)
if err != nil && !errors.IsS3Error(err, errors.ErrNoSuchKey) {
h.logAndSendError(w, "could not get object tag set", reqInfo, err)
return
}
writeHeaders(w.Header(), info, len(tagSet))
if params != nil {
writeRangeHeaders(w, params, info.Size)
}

View file

@ -5,6 +5,7 @@ import (
"net/http"
"github.com/nspcc-dev/neofs-s3-gw/api"
"github.com/nspcc-dev/neofs-s3-gw/api/errors"
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
"go.uber.org/zap"
)
@ -46,6 +47,11 @@ func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
h.logAndSendError(w, "could not fetch object info", reqInfo, err)
return
}
tagSet, err := h.obj.GetObjectTagging(r.Context(), inf)
if err != nil && !errors.IsS3Error(err, errors.ErrNoSuchKey) {
h.logAndSendError(w, "could not get object tag set", reqInfo, err)
return
}
if len(info.ContentType) == 0 {
buffer := bytes.NewBuffer(make([]byte, 0, sizeToDetectType))
@ -62,7 +68,7 @@ func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
info.ContentType = http.DetectContentType(buffer.Bytes())
}
writeHeaders(w.Header(), info)
writeHeaders(w.Header(), info, len(tagSet))
w.WriteHeader(http.StatusOK)
}

View file

@ -60,6 +60,38 @@ func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request
}
}
func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
reqInfo := api.GetReqInfo(r.Context())
p := &layer.HeadObjectParams{
Bucket: reqInfo.BucketName,
Object: reqInfo.ObjectName,
VersionID: reqInfo.URL.Query().Get("versionId"),
}
objInfo, err := h.obj.GetObjectInfo(r.Context(), p)
if err != nil {
h.logAndSendError(w, "could not get object info", reqInfo, err)
return
}
tagSet, err := h.obj.GetObjectTagging(r.Context(), objInfo)
if err != nil {
h.logAndSendError(w, "could not get object tagging", reqInfo, err)
return
}
tagging := &Tagging{}
for k, v := range tagSet {
tagging.TagSet = append(tagging.TagSet, Tag{Key: k, Value: v})
}
w.Header().Set(api.AmzVersionID, objInfo.Version())
if err = api.EncodeToResponse(w, tagging); err != nil {
h.logAndSendError(w, "something went wrong", reqInfo, err)
}
}
func checkTagSet(tagSet []Tag) error {
if len(tagSet) > 10 {
return errors.GetAPIError(errors.ErrInvalidTag)

View file

@ -31,10 +31,6 @@ func (h *handler) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Req
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
}
func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
}
func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
}

View file

@ -5,6 +5,7 @@ const (
MetadataPrefix = "X-Amz-Meta-"
AmzMetadataDirective = "X-Amz-Metadata-Directive"
AmzVersionID = "X-Amz-Version-Id"
AmzTaggingCount = "X-Amz-Tagging-Count"
LastModified = "Last-Modified"
Date = "Date"

View file

@ -162,6 +162,7 @@ type (
GetObject(ctx context.Context, p *GetObjectParams) error
GetObjectInfo(ctx context.Context, p *HeadObjectParams) (*ObjectInfo, error)
GetObjectTagging(ctx context.Context, p *ObjectInfo) (map[string]string, error)
PutObject(ctx context.Context, p *PutObjectParams) (*ObjectInfo, error)
PutObjectTagging(ctx context.Context, p *PutTaggingParams) error
@ -371,6 +372,32 @@ func (n *layer) PutObject(ctx context.Context, p *PutObjectParams) (*ObjectInfo,
return n.objectPut(ctx, bkt, p)
}
// GetObjectTagging from storage.
func (n *layer) GetObjectTagging(ctx context.Context, oi *ObjectInfo) (map[string]string, error) {
bktInfo := &BucketInfo{
Name: oi.Bucket,
CID: oi.CID(),
Owner: oi.Owner,
}
objInfo, err := n.getSystemObject(ctx, bktInfo, oi.TagsObject())
if err != nil && !errors.IsS3Error(err, errors.ErrNoSuchKey) {
return nil, err
}
var tagSet map[string]string
if objInfo != nil {
tagSet = make(map[string]string, len(objInfo.Headers))
for k, v := range objInfo.Headers {
if strings.HasPrefix(k, tagPrefix) {
tagSet[strings.TrimPrefix(k, tagPrefix)] = v
}
}
}
return tagSet, nil
}
// PutObjectTagging into storage.
func (n *layer) PutObjectTagging(ctx context.Context, p *PutTaggingParams) error {
bktInfo := &cache.BucketInfo{
@ -438,6 +465,20 @@ func (n *layer) putSystemObject(ctx context.Context, bktInfo *cache.BucketInfo,
return oid, nil
}
func (n *layer) getSystemObject(ctx context.Context, bkt *BucketInfo, objName string) (*ObjectInfo, error) {
oid, err := n.objectFindID(ctx, &findParams{cid: bkt.CID, attr: objectSystemAttributeName, val: objName})
if err != nil {
return nil, err
}
meta, err := n.objectHead(ctx, bkt.CID, oid)
if err != nil {
return nil, err
}
return objectInfoFromMeta(bkt, meta, "", ""), nil
}
// CopyObject from one bucket into another bucket.
func (n *layer) CopyObject(ctx context.Context, p *CopyObjectParams) (*ObjectInfo, error) {
pr, pw := io.Pipe()

View file

@ -69,7 +69,7 @@ Should be supported soon.
| | Method | Comments |
|----|---------------------|----------|
| 🔴 | DeleteObjectTagging | |
| 🔴 | GetObjectTagging | |
| 🟢 | GetObjectTagging | |
| 🟢 | PutObjectTagging | |
## Versioning