forked from TrueCloudLab/frostfs-s3-gw
[#196] Add GetObjectTagging
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
8b5ebe2ec2
commit
4a2575fbf3
7 changed files with 92 additions and 8 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -69,7 +69,7 @@ Should be supported soon.
|
|||
| | Method | Comments |
|
||||
|----|---------------------|----------|
|
||||
| 🔴 | DeleteObjectTagging | |
|
||||
| 🔴 | GetObjectTagging | |
|
||||
| 🟢 | GetObjectTagging | |
|
||||
| 🟢 | PutObjectTagging | |
|
||||
|
||||
## Versioning
|
||||
|
|
Loading…
Reference in a new issue