mirror of https://github.com/minio/minio.git
Listen bucket notification for multiple prefixes/suffixes (#2911)
* Listen bucket notification for multiple prefixes/suffixes * After review fixes
This commit is contained in:
parent
6199aa0707
commit
73982c8cb6
|
@ -80,9 +80,19 @@ func getObjectResources(values url.Values) (uploadID string, partNumberMarker, m
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse listen bucket notification resources.
|
// Parse listen bucket notification resources.
|
||||||
func getListenBucketNotificationResources(values url.Values) (prefix string, suffix string, events []string) {
|
func getListenBucketNotificationResources(values url.Values) (prefix []string, suffix []string, events []string) {
|
||||||
prefix = values.Get("prefix")
|
prefix = values["prefix"]
|
||||||
suffix = values.Get("suffix")
|
suffix = values["suffix"]
|
||||||
events = values["events"]
|
events = values["events"]
|
||||||
return prefix, suffix, events
|
return prefix, suffix, events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validates filter values
|
||||||
|
func validateFilterValues(values []string) (err APIErrorCode) {
|
||||||
|
for _, value := range values {
|
||||||
|
if !IsValidObjectPrefix(value) {
|
||||||
|
return ErrFilterValueInvalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ErrNone
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -188,3 +189,38 @@ func TestGetObjectsResources(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validates if filter values are correct
|
||||||
|
func TestValidateFilterValues(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
values []string
|
||||||
|
expectedError APIErrorCode
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
values: []string{""},
|
||||||
|
expectedError: ErrNone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
values: []string{"", "prefix"},
|
||||||
|
expectedError: ErrNone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
values: []string{strings.Repeat("a", 1025)},
|
||||||
|
expectedError: ErrFilterValueInvalid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
values: []string{"a\\b"},
|
||||||
|
expectedError: ErrFilterValueInvalid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
values: []string{string([]byte{0xff, 0xfe, 0xfd})},
|
||||||
|
expectedError: ErrFilterValueInvalid,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, testCase := range testCases {
|
||||||
|
if actualError := validateFilterValues(testCase.values); actualError != testCase.expectedError {
|
||||||
|
t.Errorf("Test %d: Expected %d, got %d", i+1, testCase.expectedError, actualError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -250,9 +250,15 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
// Parse listen bucket notification resources.
|
// Parse listen bucket notification resources.
|
||||||
prefix, suffix, events := getListenBucketNotificationResources(r.URL.Query())
|
prefixes, suffixes, events := getListenBucketNotificationResources(r.URL.Query())
|
||||||
if !IsValidObjectPrefix(prefix) || !IsValidObjectPrefix(suffix) {
|
|
||||||
writeErrorResponse(w, r, ErrFilterValueInvalid, r.URL.Path)
|
if err := validateFilterValues(prefixes); err != ErrNone {
|
||||||
|
writeErrorResponse(w, r, err, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateFilterValues(suffixes); err != ErrNone {
|
||||||
|
writeErrorResponse(w, r, err, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,13 +285,15 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
|
||||||
globalMinioAddr,
|
globalMinioAddr,
|
||||||
)
|
)
|
||||||
var filterRules []filterRule
|
var filterRules []filterRule
|
||||||
if prefix != "" {
|
|
||||||
|
for _, prefix := range prefixes {
|
||||||
filterRules = append(filterRules, filterRule{
|
filterRules = append(filterRules, filterRule{
|
||||||
Name: "prefix",
|
Name: "prefix",
|
||||||
Value: prefix,
|
Value: prefix,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if suffix != "" {
|
|
||||||
|
for _, suffix := range suffixes {
|
||||||
filterRules = append(filterRules, filterRule{
|
filterRules = append(filterRules, filterRule{
|
||||||
Name: "suffix",
|
Name: "suffix",
|
||||||
Value: suffix,
|
Value: suffix,
|
||||||
|
|
|
@ -235,24 +235,24 @@ func testListenBucketNotificationHandler(obj ObjectLayer, instanceType string, t
|
||||||
invalidEvents := []string{"invalidEvent"}
|
invalidEvents := []string{"invalidEvent"}
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
bucketName string
|
bucketName string
|
||||||
prefix string
|
prefixes []string
|
||||||
suffix string
|
suffixes []string
|
||||||
events []string
|
events []string
|
||||||
kind testKind
|
kind testKind
|
||||||
expectedHTTPCode int
|
expectedHTTPCode int
|
||||||
expectedAPIError string
|
expectedAPIError string
|
||||||
}{
|
}{
|
||||||
// FIXME: Need to find a way to run valid listen bucket notification test case without blocking the unit test.
|
// FIXME: Need to find a way to run valid listen bucket notification test case without blocking the unit test.
|
||||||
{randBucket, "", "", invalidEvents, CheckStatus, signatureMismatchError.HTTPStatusCode, ""},
|
{randBucket, []string{}, []string{}, invalidEvents, CheckStatus, signatureMismatchError.HTTPStatusCode, ""},
|
||||||
{randBucket, tooBigPrefix, "", validEvents, CheckStatus, http.StatusBadRequest, ""},
|
{randBucket, []string{tooBigPrefix}, []string{}, validEvents, CheckStatus, http.StatusBadRequest, ""},
|
||||||
{invalidBucket, "", "", validEvents, CheckStatus, http.StatusBadRequest, ""},
|
{invalidBucket, []string{}, []string{}, validEvents, CheckStatus, http.StatusBadRequest, ""},
|
||||||
{randBucket, "", "", validEvents, InvalidAuth, signatureMismatchError.HTTPStatusCode, signatureMismatchError.Code},
|
{randBucket, []string{}, []string{}, validEvents, InvalidAuth, signatureMismatchError.HTTPStatusCode, signatureMismatchError.Code},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range testCases {
|
for i, test := range testCases {
|
||||||
testRec = httptest.NewRecorder()
|
testRec = httptest.NewRecorder()
|
||||||
testReq, tErr = newTestSignedRequestV4("GET",
|
testReq, tErr = newTestSignedRequestV4("GET",
|
||||||
getListenBucketNotificationURL("", test.bucketName, test.prefix, test.suffix, test.events),
|
getListenBucketNotificationURL("", test.bucketName, test.prefixes, test.suffixes, test.events),
|
||||||
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
t.Fatalf("%s: Failed to create HTTP testRequest for ListenBucketNotification: <ERROR> %v", instanceType, tErr)
|
t.Fatalf("%s: Failed to create HTTP testRequest for ListenBucketNotification: <ERROR> %v", instanceType, tErr)
|
||||||
|
@ -301,7 +301,7 @@ func testListenBucketNotificationHandler(obj ObjectLayer, instanceType string, t
|
||||||
})
|
})
|
||||||
testRec = httptest.NewRecorder()
|
testRec = httptest.NewRecorder()
|
||||||
testReq, tErr = newTestSignedRequestV4("GET",
|
testReq, tErr = newTestSignedRequestV4("GET",
|
||||||
getListenBucketNotificationURL("", randBucket, "", "*.jpg", []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:*"}),
|
getListenBucketNotificationURL("", randBucket, []string{}, []string{"*.jpg"}, []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:*"}),
|
||||||
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
t.Fatalf("%s: Failed to create HTTP testRequest for ListenBucketNotification: <ERROR> %v", instanceType, tErr)
|
t.Fatalf("%s: Failed to create HTTP testRequest for ListenBucketNotification: <ERROR> %v", instanceType, tErr)
|
||||||
|
|
|
@ -1402,10 +1402,11 @@ func getGetBucketNotificationURL(endPoint, bucketName string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// return URL for listen bucket notification.
|
// return URL for listen bucket notification.
|
||||||
func getListenBucketNotificationURL(endPoint, bucketName, prefix, suffix string, events []string) string {
|
func getListenBucketNotificationURL(endPoint, bucketName string, prefixes, suffixes, events []string) string {
|
||||||
queryValue := url.Values{}
|
queryValue := url.Values{}
|
||||||
queryValue.Set("prefix", prefix)
|
|
||||||
queryValue.Set("suffix", suffix)
|
queryValue["prefix"] = prefixes
|
||||||
|
queryValue["suffix"] = suffixes
|
||||||
queryValue["events"] = events
|
queryValue["events"] = events
|
||||||
return makeTestTargetURL(endPoint, bucketName, "", queryValue)
|
return makeTestTargetURL(endPoint, bucketName, "", queryValue)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue