diff --git a/api/errors/errors.go b/api/errors/errors.go index ae548d3..33ec96e 100644 --- a/api/errors/errors.go +++ b/api/errors/errors.go @@ -73,6 +73,7 @@ const ( ErrInvalidArgument ErrInvalidTagKey ErrInvalidTagValue + ErrInvalidTagKeyUniqueness ErrInvalidTagsSizeExceed ErrNotImplemented ErrPreconditionFailed @@ -526,13 +527,19 @@ var errorCodes = errorCodeMap{ ErrInvalidTagKey: { ErrCode: ErrInvalidTagKey, Code: "InvalidTag", - Description: "The TagValue you have provided is invalid", + Description: "The TagKey you have provided is invalid", HTTPStatusCode: http.StatusBadRequest, }, ErrInvalidTagValue: { ErrCode: ErrInvalidTagValue, Code: "InvalidTag", - Description: "The TagKey you have provided is invalid", + Description: "The TagValue you have provided is invalid", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidTagKeyUniqueness: { + ErrCode: ErrInvalidTagKeyUniqueness, + Code: "InvalidTag", + Description: "Cannot provide multiple Tags with the same key", HTTPStatusCode: http.StatusBadRequest, }, ErrInvalidTagsSizeExceed: { diff --git a/api/handler/tagging.go b/api/handler/tagging.go index 306d8c1..c313ffd 100644 --- a/api/handler/tagging.go +++ b/api/handler/tagging.go @@ -220,6 +220,9 @@ func readTagSet(reader io.Reader) (map[string]string, error) { tagSet := make(map[string]string, len(tagging.TagSet)) for _, tag := range tagging.TagSet { + if _, ok := tagSet[tag.Key]; ok { + return nil, errors.GetAPIError(errors.ErrInvalidTagKeyUniqueness) + } tagSet[tag.Key] = tag.Value } diff --git a/api/handler/tagging_test.go b/api/handler/tagging_test.go index 98b39a7..27ef7be 100644 --- a/api/handler/tagging_test.go +++ b/api/handler/tagging_test.go @@ -1,9 +1,11 @@ package handler import ( + "net/http" "strings" "testing" + apiErrors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" "github.com/stretchr/testify/require" ) @@ -44,3 +46,66 @@ func TestTagsValidity(t *testing.T) { } } } + +func TestPutObjectTaggingCheckUniqueness(t *testing.T) { + hc := prepareHandlerContext(t) + + bktName, objName := "bucket-1", "object-1" + createBucketAndObject(hc, bktName, objName) + + for _, tc := range []struct { + name string + body *Tagging + error bool + }{ + { + name: "Two tags with unique keys", + body: &Tagging{ + TagSet: []Tag{ + { + Key: "key-1", + Value: "val-1", + }, + { + Key: "key-2", + Value: "val-2", + }, + }, + }, + error: false, + }, + { + name: "Two tags with the same keys", + body: &Tagging{ + TagSet: []Tag{ + { + Key: "key-1", + Value: "val-1", + }, + { + Key: "key-1", + Value: "val-2", + }, + }, + }, + error: true, + }, + } { + t.Run(tc.name, func(t *testing.T) { + w, r := prepareTestRequest(hc, bktName, objName, tc.body) + hc.Handler().PutObjectTaggingHandler(w, r) + if tc.error { + assertS3Error(t, w, apiErrors.GetAPIError(apiErrors.ErrInvalidTagKeyUniqueness)) + return + } + assertStatus(t, w, http.StatusOK) + + tagging := getObjectTagging(t, hc, bktName, objName, emptyVersion) + require.Len(t, tagging.TagSet, 2) + require.Equal(t, "key-1", tagging.TagSet[0].Key) + require.Equal(t, "val-1", tagging.TagSet[0].Value) + require.Equal(t, "key-2", tagging.TagSet[1].Key) + require.Equal(t, "val-2", tagging.TagSet[1].Value) + }) + } +}