chore: initial import from sless/shared-sqs (v0.1.14)
- Standalone SQS-service repository - Multi-tenant message queue service, AWS SQS compatible - Based on GoAws, with mutable tenants, auth, WebUI, Redis persistence - Ready for independent development and deployment - See doc/ and README.md for architecture and usage
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"shared-sqs/app/models"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gorilla/schema"
|
||||
)
|
||||
|
||||
var XmlDecoder *schema.Decoder
|
||||
var REQUEST_TRANSFORMER = TransformRequest
|
||||
|
||||
func init() {
|
||||
XmlDecoder = schema.NewDecoder()
|
||||
XmlDecoder.IgnoreUnknownKeys(true)
|
||||
}
|
||||
|
||||
func TransformRequest(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
switch req.Header.Get("Content-Type") {
|
||||
case "application/x-amz-json-1.0":
|
||||
//Read body data to parse json
|
||||
decoder := json.NewDecoder(req.Body)
|
||||
err := decoder.Decode(resultingStruct)
|
||||
if err != nil {
|
||||
if emptyRequestValid && err == io.EOF {
|
||||
return true
|
||||
}
|
||||
log.Debugf("TransformRequest Failure - %s", err.Error())
|
||||
return false
|
||||
}
|
||||
default:
|
||||
err := req.ParseForm()
|
||||
if err != nil {
|
||||
log.Debugf("TransformRequest Failure - %s", err.Error())
|
||||
return false
|
||||
}
|
||||
err = XmlDecoder.Decode(resultingStruct, req.PostForm)
|
||||
if err != nil {
|
||||
log.Debugf("TransformRequest Failure - %s", err.Error())
|
||||
return false
|
||||
}
|
||||
resultingStruct.SetAttributesFromForm(req.PostForm)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func ExtractQueueAttributes(u url.Values) map[string]string {
|
||||
attr := map[string]string{}
|
||||
for i := 1; true; i++ {
|
||||
nameKey := fmt.Sprintf("Attribute.%d.Name", i)
|
||||
attrName := u.Get(nameKey)
|
||||
if attrName == "" {
|
||||
break
|
||||
}
|
||||
|
||||
valueKey := fmt.Sprintf("Attribute.%d.Value", i)
|
||||
attrValue := u.Get(valueKey)
|
||||
if attrValue != "" {
|
||||
attr[attrName] = attrValue
|
||||
}
|
||||
}
|
||||
return attr
|
||||
}
|
||||
|
||||
func CreateErrorResponseV1(errKey string, isSqs bool) (int, interfaces.AbstractResponseBody) {
|
||||
var err interfaces.AbstractErrorResponse
|
||||
if isSqs {
|
||||
err = models.SqsErrors[errKey]
|
||||
} else {
|
||||
err = models.SnsErrors[errKey]
|
||||
}
|
||||
|
||||
respStruct := models.ErrorResponse{
|
||||
Result: err.Response(),
|
||||
RequestId: "00000000-0000-0000-0000-000000000000",
|
||||
}
|
||||
return err.StatusCode(), respStruct
|
||||
}
|
||||
|
||||
func GetMD5Hash(text string) string {
|
||||
hasher := md5.New()
|
||||
hasher.Write([]byte(text))
|
||||
return hex.EncodeToString(hasher.Sum(nil))
|
||||
}
|
||||
|
||||
func HashAttributes(attributes map[string]models.MessageAttribute) string {
|
||||
hasher := md5.New()
|
||||
|
||||
keys := sortedKeys(attributes)
|
||||
for _, key := range keys {
|
||||
attributeValue := attributes[key]
|
||||
|
||||
addStringToHash(hasher, key)
|
||||
addStringToHash(hasher, attributeValue.DataType)
|
||||
if attributeValue.DataType == "String" {
|
||||
hasher.Write([]byte{1})
|
||||
addStringToHash(hasher, attributeValue.StringValue)
|
||||
} else if attributeValue.DataType == "Binary" {
|
||||
hasher.Write([]byte{2})
|
||||
bytes, _ := base64.StdEncoding.DecodeString(attributeValue.BinaryValue)
|
||||
addBytesToHash(hasher, []byte(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
return hex.EncodeToString(hasher.Sum(nil))
|
||||
}
|
||||
|
||||
func sortedKeys(attributes map[string]models.MessageAttribute) []string {
|
||||
var keys []string
|
||||
for key := range attributes {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func addStringToHash(hasher hash.Hash, str string) {
|
||||
bytes := []byte(str)
|
||||
addBytesToHash(hasher, bytes)
|
||||
}
|
||||
|
||||
func addBytesToHash(hasher hash.Hash, arr []byte) {
|
||||
bs := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(bs, uint32(len(arr)))
|
||||
hasher.Write(bs)
|
||||
hasher.Write(arr)
|
||||
}
|
||||
|
||||
func HasFIFOQueueName(queueName string) bool {
|
||||
return strings.HasSuffix(queueName, ".fifo")
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"shared-sqs/app/models"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/mocks"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTransformRequest_success_json(t *testing.T) {
|
||||
_, r := test.GenerateRequestInfo("POST", "url", fixtures.JSONRequestBody, true)
|
||||
|
||||
mock := &mocks.MockRequestBody{}
|
||||
|
||||
ok := TransformRequest(mock, r, false)
|
||||
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "mock-value", mock.RequestFieldStr)
|
||||
assert.False(t, mock.SetAttributesFromFormCalled)
|
||||
}
|
||||
|
||||
func TestTransformRequest_success_json_empty_request_accepted(t *testing.T) {
|
||||
_, r := test.GenerateRequestInfo("POST", "url", nil, true)
|
||||
|
||||
mock := &mocks.MockRequestBody{}
|
||||
|
||||
ok := TransformRequest(mock, r, true)
|
||||
|
||||
assert.True(t, ok)
|
||||
//assert.Equal(t, "mock-value", mock.RequestFieldStr)
|
||||
assert.False(t, mock.SetAttributesFromFormCalled)
|
||||
}
|
||||
|
||||
func TestTransformRequest_success_xml(t *testing.T) {
|
||||
_, r := test.GenerateRequestInfo("POST", "url", nil, false)
|
||||
form := url.Values{}
|
||||
form.Add("Action", "CreateQueue")
|
||||
form.Add("QueueName", "UnitTestQueue1")
|
||||
form.Add("Attribute.1.Name", "VisibilityTimeout")
|
||||
form.Add("Attribute.1.Value", "60")
|
||||
form.Add("Attribute.2.Name", "MaximumMessageSize")
|
||||
form.Add("Attribute.2.Value", "2048")
|
||||
r.PostForm = form
|
||||
|
||||
mock := &mocks.MockRequestBody{}
|
||||
|
||||
ok := TransformRequest(mock, r, false)
|
||||
|
||||
assert.True(t, ok)
|
||||
assert.True(t, mock.SetAttributesFromFormCalled)
|
||||
assert.Equal(t, []interface{}{form}, mock.SetAttributesFromFormCalledWith)
|
||||
}
|
||||
|
||||
func TestTransformRequest_error_invalid_request_body_json(t *testing.T) {
|
||||
_, r := test.GenerateRequestInfo("POST", "url", "\"I-am-garbage", true)
|
||||
|
||||
mock := &mocks.MockRequestBody{}
|
||||
|
||||
ok := TransformRequest(mock, r, false)
|
||||
|
||||
assert.False(t, ok)
|
||||
assert.Equal(t, "", mock.RequestFieldStr)
|
||||
assert.False(t, mock.SetAttributesFromFormCalled)
|
||||
}
|
||||
|
||||
func TestTransformRequest_error_failure_to_parse_form_xml(t *testing.T) {
|
||||
_, r := test.GenerateRequestInfo("POST", "url", nil, false)
|
||||
|
||||
mock := &mocks.MockRequestBody{}
|
||||
|
||||
ok := TransformRequest(mock, r, false)
|
||||
|
||||
assert.False(t, ok)
|
||||
assert.False(t, mock.SetAttributesFromFormCalled)
|
||||
}
|
||||
|
||||
func TestTransformRequest_error_invalid_request_body_xml(t *testing.T) {
|
||||
_, r := test.GenerateRequestInfo("POST", "url", nil, false)
|
||||
|
||||
form := url.Values{}
|
||||
form.Add("intField", "\"I-am-garbage")
|
||||
r.PostForm = form
|
||||
|
||||
mock := &mocks.MockRequestBody{}
|
||||
|
||||
ok := TransformRequest(mock, r, false)
|
||||
|
||||
assert.False(t, ok)
|
||||
assert.False(t, mock.SetAttributesFromFormCalled)
|
||||
}
|
||||
|
||||
func TestExtractQueueAttributes_success(t *testing.T) {
|
||||
u := url.Values{}
|
||||
u.Add("Attribute.1.Name", "DelaySeconds")
|
||||
u.Add("Attribute.1.Value", "20")
|
||||
u.Add("Attribute.2.Name", "VisibilityTimeout")
|
||||
u.Add("Attribute.2.Value", "30")
|
||||
u.Add("Attribute.3.Name", "Policy")
|
||||
|
||||
attr := ExtractQueueAttributes(u)
|
||||
expected := map[string]string{
|
||||
"DelaySeconds": "20",
|
||||
"VisibilityTimeout": "30",
|
||||
}
|
||||
|
||||
assert.Equal(t, expected, attr)
|
||||
}
|
||||
|
||||
func TestGetMD5Hash(t *testing.T) {
|
||||
hash1 := GetMD5Hash("This is a test")
|
||||
hash2 := GetMD5Hash("This is a test")
|
||||
if hash1 != hash2 {
|
||||
t.Errorf("hashs and hash2 should be the same, but were not")
|
||||
}
|
||||
|
||||
hash1 = GetMD5Hash("This is a test")
|
||||
hash2 = GetMD5Hash("This is a tfst")
|
||||
if hash1 == hash2 {
|
||||
t.Errorf("hashs and hash2 are the same, but should not be")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortedKeys(t *testing.T) {
|
||||
attributes := map[string]models.MessageAttribute{
|
||||
"b": {},
|
||||
"a": {},
|
||||
}
|
||||
|
||||
keys := sortedKeys(attributes)
|
||||
assert.Equal(t, "a", keys[0])
|
||||
assert.Equal(t, "b", keys[1])
|
||||
}
|
||||
Reference in New Issue
Block a user