[#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
|
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 {
|
if len(info.ContentType) > 0 {
|
||||||
h.Set(api.ContentType, info.ContentType)
|
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.ContentLength, strconv.FormatInt(info.Size, 10))
|
||||||
h.Set(api.ETag, info.HashSum)
|
h.Set(api.ETag, info.HashSum)
|
||||||
h.Set(api.AmzVersionID, info.ID().String())
|
h.Set(api.AmzVersionID, info.ID().String())
|
||||||
|
h.Set(api.AmzTaggingCount, strconv.Itoa(tagSetLength))
|
||||||
|
|
||||||
for key, val := range info.Headers {
|
for key, val := range info.Headers {
|
||||||
h[api.MetadataPrefix+key] = []string{val}
|
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)
|
h.logAndSendError(w, "could not parse range header", reqInfo, err)
|
||||||
return
|
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 {
|
if params != nil {
|
||||||
writeRangeHeaders(w, params, info.Size)
|
writeRangeHeaders(w, params, info.Size)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"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"
|
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
||||||
"go.uber.org/zap"
|
"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)
|
h.logAndSendError(w, "could not fetch object info", reqInfo, err)
|
||||||
return
|
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 {
|
if len(info.ContentType) == 0 {
|
||||||
buffer := bytes.NewBuffer(make([]byte, 0, sizeToDetectType))
|
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())
|
info.ContentType = http.DetectContentType(buffer.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
writeHeaders(w.Header(), info)
|
writeHeaders(w.Header(), info, len(tagSet))
|
||||||
w.WriteHeader(http.StatusOK)
|
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 {
|
func checkTagSet(tagSet []Tag) error {
|
||||||
if len(tagSet) > 10 {
|
if len(tagSet) > 10 {
|
||||||
return errors.GetAPIError(errors.ErrInvalidTag)
|
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))
|
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) {
|
func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented))
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ const (
|
||||||
MetadataPrefix = "X-Amz-Meta-"
|
MetadataPrefix = "X-Amz-Meta-"
|
||||||
AmzMetadataDirective = "X-Amz-Metadata-Directive"
|
AmzMetadataDirective = "X-Amz-Metadata-Directive"
|
||||||
AmzVersionID = "X-Amz-Version-Id"
|
AmzVersionID = "X-Amz-Version-Id"
|
||||||
|
AmzTaggingCount = "X-Amz-Tagging-Count"
|
||||||
|
|
||||||
LastModified = "Last-Modified"
|
LastModified = "Last-Modified"
|
||||||
Date = "Date"
|
Date = "Date"
|
||||||
|
|
|
@ -162,6 +162,7 @@ type (
|
||||||
|
|
||||||
GetObject(ctx context.Context, p *GetObjectParams) error
|
GetObject(ctx context.Context, p *GetObjectParams) error
|
||||||
GetObjectInfo(ctx context.Context, p *HeadObjectParams) (*ObjectInfo, 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)
|
PutObject(ctx context.Context, p *PutObjectParams) (*ObjectInfo, error)
|
||||||
PutObjectTagging(ctx context.Context, p *PutTaggingParams) 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)
|
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.
|
// PutObjectTagging into storage.
|
||||||
func (n *layer) PutObjectTagging(ctx context.Context, p *PutTaggingParams) error {
|
func (n *layer) PutObjectTagging(ctx context.Context, p *PutTaggingParams) error {
|
||||||
bktInfo := &cache.BucketInfo{
|
bktInfo := &cache.BucketInfo{
|
||||||
|
@ -438,6 +465,20 @@ func (n *layer) putSystemObject(ctx context.Context, bktInfo *cache.BucketInfo,
|
||||||
return oid, nil
|
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.
|
// CopyObject from one bucket into another bucket.
|
||||||
func (n *layer) CopyObject(ctx context.Context, p *CopyObjectParams) (*ObjectInfo, error) {
|
func (n *layer) CopyObject(ctx context.Context, p *CopyObjectParams) (*ObjectInfo, error) {
|
||||||
pr, pw := io.Pipe()
|
pr, pw := io.Pipe()
|
||||||
|
|
|
@ -69,7 +69,7 @@ Should be supported soon.
|
||||||
| | Method | Comments |
|
| | Method | Comments |
|
||||||
|----|---------------------|----------|
|
|----|---------------------|----------|
|
||||||
| 🔴 | DeleteObjectTagging | |
|
| 🔴 | DeleteObjectTagging | |
|
||||||
| 🔴 | GetObjectTagging | |
|
| 🟢 | GetObjectTagging | |
|
||||||
| 🟢 | PutObjectTagging | |
|
| 🟢 | PutObjectTagging | |
|
||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|
Loading…
Reference in a new issue