Skip to content

Commit df011ca

Browse files
committed
#97: Make receive-message match sqs behaviour
SQS only includes message attributes if they are specified in the recieve message request, while ElasticMQ was returning all attributes by default. I have updated the ReceiveMessageDirective to respect the messageAttributeName parameter and to no longer include attributes unless specified.
1 parent 3154233 commit df011ca

File tree

2 files changed

+81
-10
lines changed

2 files changed

+81
-10
lines changed

rest/rest-sqs-testing-amazon-java-sdk/src/test/scala/org/elasticmq/rest/sqs/AmazonJavaSdkTestSuite.scala

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import com.amazonaws.auth.BasicAWSCredentials
88
import com.amazonaws.services.sqs.{AmazonSQS, AmazonSQSClient}
99

1010
import scala.collection.JavaConversions._
11+
import scala.collection.JavaConverters._
12+
1113
import com.amazonaws.services.sqs.model._
1214
import scala.util.control.Exception._
1315
import com.amazonaws.AmazonServiceException
@@ -169,6 +171,24 @@ class AmazonJavaSdkTestSuite extends FunSuite with Matchers with BeforeAndAfter
169171
))
170172
}
171173

174+
test("should send a simple message with message attributes and only receive requested attributes") {
175+
doTestSendAndReceiveMessageWithAttributes("Message 1", Map(
176+
"red" -> StringMessageAttribute("fish"),
177+
"blue" -> StringMessageAttribute("cat"),
178+
"green" -> BinaryMessageAttribute("dog".getBytes("UTF-8")),
179+
"yellow" -> NumberMessageAttribute("1234567890")
180+
), List("red", "green"))
181+
}
182+
183+
test("should send a simple message with message attributes and only receive no requested attributes by default") {
184+
doTestSendAndReceiveMessageWithAttributes("Message 1", Map(
185+
"red" -> StringMessageAttribute("fish"),
186+
"blue" -> StringMessageAttribute("cat"),
187+
"green" -> BinaryMessageAttribute("dog".getBytes("UTF-8")),
188+
"yellow" -> NumberMessageAttribute("1234567890")
189+
), List())
190+
}
191+
172192
test("should send and receive a message with caret return and new line characters") {
173193
doTestSendAndReceiveMessage("a\rb\r\nc\nd")
174194
}
@@ -189,7 +209,9 @@ class AmazonJavaSdkTestSuite extends FunSuite with Matchers with BeforeAndAfter
189209
doTestSendAndReceiveMessage(builder.toString())
190210
}
191211

192-
def doTestSendAndReceiveMessageWithAttributes(content: String, messageAttributes: Map[String, MessageAttribute]) {
212+
def doTestSendAndReceiveMessageWithAttributes(content: String,
213+
messageAttributes: Map[String, MessageAttribute],
214+
requestedAttributes: List[String]) {
193215
// Given
194216
val queueUrl = client.createQueue(new CreateQueueRequest("testQueue1")).getQueueUrl
195217

@@ -208,11 +230,21 @@ class AmazonJavaSdkTestSuite extends FunSuite with Matchers with BeforeAndAfter
208230
}
209231

210232
client.sendMessage(sendMessage)
211-
val message = receiveSingleMessageObject(queueUrl).orNull
233+
val message = receiveSingleMessageObject(queueUrl, requestedAttributes).orNull
212234

213235
// Then
214236
message.getBody should be(content)
215-
message.getMessageAttributes should be(sendMessage.getMessageAttributes) // Checks they match
237+
checkMessageAttributesMatchRequestedAttributes(messageAttributes, requestedAttributes, sendMessage, message)
238+
}
239+
240+
private def checkMessageAttributesMatchRequestedAttributes(messageAttributes: Map[String, MessageAttribute],
241+
requestedAttributes: List[String],
242+
sendMessage: SendMessageRequest,
243+
message: Message) = {
244+
val filteredSendMessageAttr = filterBasedOnRequestedAttributes(requestedAttributes, sendMessage.getMessageAttributes.toMap).asJava
245+
val filteredMessageAttributes = filterBasedOnRequestedAttributes(requestedAttributes, messageAttributes)
246+
247+
message.getMessageAttributes should be(filteredSendMessageAttr) // Checks they match
216248
message.getMessageAttributes.map { case (k, attr) =>
217249
(k, if (attr.getDataType.startsWith("String") && attr.getStringValue != null) {
218250
StringMessageAttribute(attr.getStringValue).stringValue
@@ -221,7 +253,7 @@ class AmazonJavaSdkTestSuite extends FunSuite with Matchers with BeforeAndAfter
221253
} else {
222254
BinaryMessageAttribute.fromByteBuffer(attr.getBinaryValue).asBase64
223255
})
224-
} should be(messageAttributes.map { case (k, attr) =>
256+
} should be(filteredMessageAttributes.map { case (k, attr) =>
225257
(k, attr match {
226258
case s: StringMessageAttribute => s.stringValue
227259
case n: NumberMessageAttribute => n.stringValue
@@ -230,11 +262,25 @@ class AmazonJavaSdkTestSuite extends FunSuite with Matchers with BeforeAndAfter
230262
}) // Checks they match map
231263
}
232264

265+
private def filterBasedOnRequestedAttributes[T](requestedAttributes: List[String], messageAttributes: Map[String, T]): Map[String, T] = {
266+
if (requestedAttributes.contains("All")) {
267+
messageAttributes
268+
} else {
269+
messageAttributes.filterKeys(k => requestedAttributes.contains(k))
270+
}
271+
}
272+
273+
233274
// Alias for send and receive with no attributes
234275
def doTestSendAndReceiveMessage(content: String) {
235-
doTestSendAndReceiveMessageWithAttributes(content, Map())
276+
doTestSendAndReceiveMessageWithAttributes(content, Map(), List())
236277
}
237278

279+
def doTestSendAndReceiveMessageWithAttributes(content: String, messageAttributes: Map[String, MessageAttribute]): Unit = {
280+
doTestSendAndReceiveMessageWithAttributes(content, messageAttributes, List("All"))
281+
}
282+
283+
238284
test("should receive two messages in a batch") {
239285
// Given
240286
val queueUrl = client.createQueue(new CreateQueueRequest("testQueue1")).getQueueUrl
@@ -926,7 +972,11 @@ class AmazonJavaSdkTestSuite extends FunSuite with Matchers with BeforeAndAfter
926972
.toLong
927973
}
928974

929-
def receiveSingleMessage(queueUrl: String) = {
975+
def receiveSingleMessage(queueUrl: String): Option[String] = {
976+
receiveSingleMessage(queueUrl, List("All"))
977+
}
978+
979+
def receiveSingleMessage(queueUrl: String, requestedAttributes: List[String]): Option[String] = {
930980
val messages = client.receiveMessage(new ReceiveMessageRequest(queueUrl)).getMessages
931981
if (messages.size() == 0) {
932982
None
@@ -935,8 +985,12 @@ class AmazonJavaSdkTestSuite extends FunSuite with Matchers with BeforeAndAfter
935985
}
936986
}
937987

938-
def receiveSingleMessageObject(queueUrl: String) = {
939-
val messages = client.receiveMessage(new ReceiveMessageRequest(queueUrl)).getMessages
988+
def receiveSingleMessageObject(queueUrl: String): Option[Message] = {
989+
receiveSingleMessageObject(queueUrl, List("All"))
990+
}
991+
992+
def receiveSingleMessageObject(queueUrl: String, requestedAttributes: List[String]): Option[Message] = {
993+
val messages = client.receiveMessage(new ReceiveMessageRequest(queueUrl).withMessageAttributeNames(requestedAttributes)).getMessages
940994
if (messages.size() == 0) {
941995
None
942996
} else {

rest/rest-sqs/src/main/scala/org/elasticmq/rest/sqs/ReceiveMessageDirectives.scala

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ trait ReceiveMessageDirectives { this: ElasticMQDirectives with AttributesModule
1616
val SenderIdAttribute = "SenderId"
1717
val MaxNumberOfMessagesAttribute = "MaxNumberOfMessages"
1818
val WaitTimeSecondsAttribute = "WaitTimeSeconds"
19+
val MessageAttributeNamePattern = "MessageAttributeName\\.\\d".r
1920

2021
val AllAttributeNames = SentTimestampAttribute :: ApproximateReceiveCountAttribute ::
2122
ApproximateFirstReceiveTimestampAttribute :: SenderIdAttribute :: Nil
@@ -38,6 +39,8 @@ trait ReceiveMessageDirectives { this: ElasticMQDirectives with AttributesModule
3839

3940
val waitTimeSecondsFromParameters = waitTimeSecondsAttributeOpt.map(Duration.standardSeconds)
4041

42+
val messageAttributeNames = getMessageAttributeNames(p)
43+
4144
ifStrictLimits(maxNumberOfMessagesFromParameters < 1 || maxNumberOfMessagesFromParameters > 10) {
4245
"ReadCountOutOfRange"
4346
}
@@ -64,20 +67,29 @@ trait ReceiveMessageDirectives { this: ElasticMQDirectives with AttributesModule
6467
}).toString))
6568
}
6669

70+
def getFilteredAttributeNames(messageAttributeNames: Iterable[String], msg: MessageData) = {
71+
if (messageAttributeNames.exists(s => s == "All" || s == ".*")) {
72+
msg.messageAttributes
73+
} else {
74+
msg.messageAttributes.filterKeys(k => messageAttributeNames.exists(s => s == k || k.r.findFirstIn(s).isDefined))
75+
}
76+
}
77+
6778
msgsFuture.map { msgs =>
6879
respondWith {
6980
<ReceiveMessageResponse>
7081
<ReceiveMessageResult>
7182
{msgs.map { msg =>
7283
val receipt = msg.deliveryReceipt.map(_.receipt).getOrElse(throw new RuntimeException("No receipt for a received msg."))
84+
val filteredMessageAttributes = getFilteredAttributeNames(messageAttributeNames, msg)
7385
<Message>
7486
<MessageId>{msg.id.id}</MessageId>
7587
<ReceiptHandle>{receipt}</ReceiptHandle>
7688
<MD5OfBody>{md5Digest(msg.content)}</MD5OfBody>
7789
<Body>{XmlUtil.convertTexWithCRToNodeSeq(msg.content)}</Body>
7890
{attributesToXmlConverter.convert(calculateAttributeValues(msg))}
79-
<MD5OfMessageAttributes>{md5AttributeDigest(msg.messageAttributes)}</MD5OfMessageAttributes>
80-
{messageAttributesToXmlConverter.convert(msg.messageAttributes.toList)}
91+
<MD5OfMessageAttributes>{md5AttributeDigest(filteredMessageAttributes)}</MD5OfMessageAttributes>
92+
{messageAttributesToXmlConverter.convert(filteredMessageAttributes.toList)}
8193
</Message> }}
8294
</ReceiveMessageResult>
8395
<ResponseMetadata>
@@ -89,4 +101,9 @@ trait ReceiveMessageDirectives { this: ElasticMQDirectives with AttributesModule
89101
}
90102
}
91103
}
104+
105+
def getMessageAttributeNames(p: AnyParams): Iterable[String] = {
106+
p.filterKeys(k => MessageReadeableAttributeNames.MessageAttributeNamePattern.findFirstIn(k).isDefined).values
107+
}
108+
92109
}

0 commit comments

Comments
 (0)