Skip to content

Commit 7c1502c

Browse files
authored
Fix JSON serialization of message attributes with a custom data type (#950)
* Fix JSON serialization of message attributes with a custom data type * Code review fix
1 parent 1056b3a commit 7c1502c

File tree

3 files changed

+167
-15
lines changed

3 files changed

+167
-15
lines changed

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ class AmazonJavaSdkTestSuite extends SqsClientServerCommunication with Matchers
166166
"red" -> StringMessageAttribute("fish"),
167167
"blue" -> StringMessageAttribute("cat"),
168168
"green" -> BinaryMessageAttribute("dog".getBytes("UTF-8")),
169-
"yellow" -> NumberMessageAttribute("1234567890")
169+
"yellow" -> NumberMessageAttribute("1234567890"),
170+
"orange" -> NumberMessageAttribute("0987654321", Some("custom"))
170171
)
171172
)
172173
}
@@ -178,9 +179,10 @@ class AmazonJavaSdkTestSuite extends SqsClientServerCommunication with Matchers
178179
"red" -> StringMessageAttribute("fish"),
179180
"blue" -> StringMessageAttribute("cat"),
180181
"green" -> BinaryMessageAttribute("dog".getBytes("UTF-8")),
181-
"yellow" -> NumberMessageAttribute("1234567890")
182+
"yellow" -> NumberMessageAttribute("1234567890"),
183+
"orange" -> NumberMessageAttribute("0987654321", Some("custom"))
182184
),
183-
List("red", "green")
185+
List("red", "green", "orange")
184186
)
185187
}
186188

@@ -191,7 +193,8 @@ class AmazonJavaSdkTestSuite extends SqsClientServerCommunication with Matchers
191193
"red" -> StringMessageAttribute("fish"),
192194
"blue" -> StringMessageAttribute("cat"),
193195
"green" -> BinaryMessageAttribute("dog".getBytes("UTF-8")),
194-
"yellow" -> NumberMessageAttribute("1234567890")
196+
"yellow" -> NumberMessageAttribute("1234567890"),
197+
"orange" -> NumberMessageAttribute("0987654321", Some("custom"))
195198
),
196199
List()
197200
)

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

Lines changed: 153 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package org.elasticmq.rest.sqs
22

3+
import org.elasticmq.{BinaryMessageAttribute, MessageAttribute, NumberMessageAttribute, StringMessageAttribute}
34
import org.scalatest.matchers.should.Matchers
5+
6+
import scala.collection.JavaConverters._
7+
import software.amazon.awssdk.core.SdkBytes
48
import software.amazon.awssdk.services.sqs.model._
59
import software.amazon.awssdk.services.sqs.model.{GetQueueUrlRequest => AwsSdkGetQueueUrlRequest}
610

@@ -32,16 +36,159 @@ class AmazonJavaSdkV2TestSuite extends SqsClientServerWithSdkV2Communication wit
3236
thrown.awsErrorDetails().errorMessage() shouldBe "The specified queue does not exist."
3337
}
3438

35-
test("should send and receive message") {
39+
test("should send and receive a simple message") {
40+
doTestSendAndReceiveMessage("test msg 123")
41+
}
42+
43+
test("should send and receive a simple message with message attributes") {
44+
doTestSendAndReceiveMessageWithAttributes(
45+
"Message 1",
46+
Map(
47+
"red" -> StringMessageAttribute("fish"),
48+
"blue" -> StringMessageAttribute("cat"),
49+
// affected by https://github.com/softwaremill/elasticmq/issues/946
50+
// "green" -> BinaryMessageAttribute("dog".getBytes("UTF-8")),
51+
"yellow" -> NumberMessageAttribute("1234567890"),
52+
"orange" -> NumberMessageAttribute("0987654321", Some("custom"))
53+
)
54+
)
55+
}
56+
57+
test("should send a simple message with message attributes and only receive requested attributes") {
58+
doTestSendAndReceiveMessageWithAttributes(
59+
"Message 1",
60+
Map(
61+
"red" -> StringMessageAttribute("fish"),
62+
"blue" -> StringMessageAttribute("cat"),
63+
// affected by https://github.com/softwaremill/elasticmq/issues/946
64+
// "green" -> BinaryMessageAttribute("dog".getBytes("UTF-8")),
65+
"yellow" -> NumberMessageAttribute("1234567890"),
66+
"orange" -> NumberMessageAttribute("0987654321", Some("custom"))
67+
),
68+
List("red", "green", "orange")
69+
)
70+
}
71+
72+
test("should send a simple message with message attributes and only receive no requested attributes by default") {
73+
doTestSendAndReceiveMessageWithAttributes(
74+
"Message 1",
75+
Map(
76+
"red" -> StringMessageAttribute("fish"),
77+
"blue" -> StringMessageAttribute("cat"),
78+
// affected by https://github.com/softwaremill/elasticmq/issues/946
79+
// "green" -> BinaryMessageAttribute("dog".getBytes("UTF-8")),
80+
"yellow" -> NumberMessageAttribute("1234567890"),
81+
"orange" -> NumberMessageAttribute("0987654321", Some("custom"))
82+
),
83+
List()
84+
)
85+
}
86+
87+
private def doTestSendAndReceiveMessage(content: String): Unit = {
88+
doTestSendAndReceiveMessageWithAttributes(content, Map(), List())
89+
}
90+
91+
private def doTestSendAndReceiveMessageWithAttributes(
92+
content: String,
93+
messageAttributes: Map[String, MessageAttribute]
94+
): Unit = {
95+
doTestSendAndReceiveMessageWithAttributes(content, messageAttributes, List("All"))
96+
}
97+
98+
private def doTestSendAndReceiveMessageWithAttributes(
99+
content: String,
100+
messageAttributes: Map[String, MessageAttribute],
101+
requestedAttributes: List[String]
102+
): Unit = {
103+
// Given
36104
val queue = clientV2.createQueue(CreateQueueRequest.builder().queueName("testQueue1").build())
37105

38-
clientV2.sendMessage(SendMessageRequest.builder().queueUrl(queue.queueUrl()).messageBody("test msg 123").build())
106+
// When
107+
val attributes = messageAttributes.map {
108+
case (k, v: StringMessageAttribute) =>
109+
k -> MessageAttributeValue.builder().dataType(v.getDataType()).stringValue(v.stringValue).build()
110+
case (k, v: NumberMessageAttribute) =>
111+
k -> MessageAttributeValue.builder().dataType(v.getDataType()).stringValue(v.stringValue).build()
112+
case (k, v: BinaryMessageAttribute) =>
113+
k -> MessageAttributeValue
114+
.builder()
115+
.dataType(v.getDataType())
116+
.binaryValue(SdkBytes.fromByteArray(v.binaryValue))
117+
.build()
118+
}
119+
120+
val sendMessageRequest = SendMessageRequest
121+
.builder()
122+
.queueUrl(queue.queueUrl())
123+
.messageBody(content)
124+
.messageAttributes(attributes.asJava)
125+
.build()
39126

40-
val messages = clientV2.receiveMessage(ReceiveMessageRequest.builder().queueUrl(queue.queueUrl()).build())
127+
clientV2.sendMessage(sendMessageRequest)
41128

42-
System.err.println(messages)
129+
val message = receiveSingleMessageObject(queue.queueUrl(), requestedAttributes).orNull
130+
131+
// Then
132+
message.body() shouldBe content
133+
checkMessageAttributesMatchRequestedAttributes(messageAttributes, requestedAttributes, sendMessageRequest, message)
134+
}
43135

44-
messages.messages().size() shouldBe 1
45-
messages.messages().get(0).body() shouldBe "test msg 123"
136+
private def receiveSingleMessageObject(queueUrl: String, requestedAttributes: List[String]): Option[Message] = {
137+
clientV2
138+
.receiveMessage(
139+
ReceiveMessageRequest
140+
.builder()
141+
.queueUrl(queueUrl)
142+
.messageAttributeNames(requestedAttributes.asJava)
143+
.build()
144+
)
145+
.messages
146+
.asScala
147+
.headOption
148+
}
149+
150+
private def checkMessageAttributesMatchRequestedAttributes(
151+
messageAttributes: Map[String, MessageAttribute],
152+
requestedAttributes: List[String],
153+
sendMessageRequest: SendMessageRequest,
154+
message: Message
155+
) = {
156+
val filteredSendMessageAttr =
157+
filterBasedOnRequestedAttributes(requestedAttributes, sendMessageRequest.messageAttributes.asScala.toMap).asJava
158+
val filteredMessageAttributes = filterBasedOnRequestedAttributes(requestedAttributes, messageAttributes)
159+
160+
message.messageAttributes should be(filteredSendMessageAttr)
161+
message.messageAttributes.asScala.map { case (k, attr) =>
162+
(
163+
k,
164+
if (attr.dataType.startsWith("String") && attr.stringValue != null) {
165+
StringMessageAttribute(attr.stringValue).stringValue
166+
} else if (attr.dataType.startsWith("Number") && attr.stringValue != null) {
167+
NumberMessageAttribute(attr.stringValue).stringValue
168+
} else {
169+
BinaryMessageAttribute.fromByteBuffer(attr.binaryValue.asByteBuffer()).asBase64
170+
}
171+
)
172+
} should be(filteredMessageAttributes.map { case (k, attr) =>
173+
(
174+
k,
175+
attr match {
176+
case s: StringMessageAttribute => s.stringValue
177+
case n: NumberMessageAttribute => n.stringValue
178+
case b: BinaryMessageAttribute => b.asBase64
179+
}
180+
)
181+
})
182+
}
183+
184+
private def filterBasedOnRequestedAttributes[T](
185+
requestedAttributes: List[String],
186+
messageAttributes: Map[String, T]
187+
): Map[String, T] = {
188+
if (requestedAttributes.contains("All")) {
189+
messageAttributes
190+
} else {
191+
messageAttributes.filterKeys(k => requestedAttributes.contains(k)).toMap
192+
}
46193
}
47194
}

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ trait MessageAttributesSupport {
1313
implicit val messageAttributeJsonFormat: RootJsonFormat[MessageAttribute] = new RootJsonFormat[MessageAttribute] {
1414

1515
override def write(obj: MessageAttribute): JsValue = obj match {
16-
case NumberMessageAttribute(value, _) =>
17-
JsObject("DataType" -> JsString("Number"), "StringValue" -> JsString(value))
18-
case StringMessageAttribute(value, _) =>
19-
JsObject("DataType" -> JsString("String"), "StringValue" -> JsString(value))
16+
case NumberMessageAttribute(value, customType) =>
17+
JsObject("DataType" -> JsString("Number" + customTypeAsString(customType)), "StringValue" -> JsString(value))
18+
case StringMessageAttribute(value, customType) =>
19+
JsObject("DataType" -> JsString("String" + customTypeAsString(customType)), "StringValue" -> JsString(value))
2020
case msg: BinaryMessageAttribute =>
21-
JsObject("DataType" -> JsString("Binary"), "BinaryValue" -> JsString(msg.asBase64))
21+
JsObject("DataType" -> JsString("Binary" + customTypeAsString(msg.customType)), "BinaryValue" -> JsString(msg.asBase64))
2222
}
2323

2424
override def read(json: JsValue): MessageAttribute = {
@@ -38,6 +38,8 @@ trait MessageAttributesSupport {
3838
}
3939

4040
private def customType(appendix: String) = if (appendix.isEmpty) None else Some(appendix)
41+
42+
private def customTypeAsString(customType: Option[String]) = customType.fold("")(t => s".$t")
4143
}
4244

4345
}

0 commit comments

Comments
 (0)