spring-cloud-aws icon indicating copy to clipboard operation
spring-cloud-aws copied to clipboard

Handling of SNS MessageAttributes in NotificationRequestConverter seems incorrect

Open maccamlcQ opened this issue 5 years ago • 0 comments

Describe the bug Spring Cloud AWS Messaging 2.1.4.RELEASE

Sample I was looking at NotificationRequestConverter#getMessageAttributesAsMessageHeaders and noticed a couple of issues.

  • Binary type is actually wrapping the output of Type, not Value. It also doesn't decode the string.
  • String.Array isn't supported.

I ended up updating my own for a similar purpose, and came up with:

private static Map<String, Object> getMessageAttributesAsMessageHeaders(final JsonNode message) {
        final Map<String, Object> messageHeaders = new HashMap<>();
        final Iterator<String> fieldNames = message.fieldNames();
        while (fieldNames.hasNext()) {
            String attributeName = fieldNames.next();
            String attributeValue = message.get(attributeName).get("Value").asText();
            String attributeType = message.get(attributeName).get("Type").asText();

            if (MessageHeaders.CONTENT_TYPE.equals(attributeName)) {
                messageHeaders.put(MessageHeaders.CONTENT_TYPE, MimeType.valueOf(attributeValue));

            } else if (MessageHeaders.ID.equals(attributeName)) {
                messageHeaders.put(MessageHeaders.ID, UUID.fromString(attributeValue));

            } else {
                if (MessageAttributeDataTypes.STRING.equals(attributeType)) {
                    messageHeaders.put(attributeName, attributeValue);
                } else if ("String.Array".equalsIgnoreCase(attributeType)) {
                    final List<?> arrayValue = getArrayValue(attributeType, attributeValue);
                    if (arrayValue != null) {
                        messageHeaders.put(attributeName, arrayValue);
                    }
                } else if (attributeType.startsWith(MessageAttributeDataTypes.NUMBER)) {
                    Object numberValue = getNumberValue(attributeType, attributeValue);
                    messageHeaders.put(attributeName, numberValue);
                } else if (MessageAttributeDataTypes.BINARY.equals(attributeType)) {
                    messageHeaders.put(attributeName, ByteBuffer.wrap(Base64.decode(attributeValue)));
                }
            }
        }

        return messageHeaders;
    }

    private static Object getNumberValue(String attributeType, String attributeValue) {
        final String numberType = attributeType.substring(MessageAttributeDataTypes.NUMBER.length() + 1);

        try {
            final Class<? extends Number> numberTypeClass = Class.forName(numberType).asSubclass(Number.class);
            return NumberUtils.parseNumber(attributeValue, numberTypeClass);

        } catch (ClassNotFoundException e) {
            throw new MessagingException(String.format(
                    "Message attribute with value '%s' and data type '%s' could not be converted "
                            + "into a Number because target class was not found.",
                    attributeValue, attributeType), e);
        }
    }

    private static List<?> getArrayValue(String attributeType, String attributeValue) {
        try {
            return objectMapper.readValue(attributeValue, List.class);
        } catch (IOException e) {
            throw new MessagingException(String.format(
                    "Message attribute with value '%s' and data type '%s' could not be converted into an Array.",
                    attributeValue, attributeType), e);
        }
    }

This handling of the Binary and String.Array types functions the way I was expecting in my tests.

Doesn't look like anyone else come across this though, so just flagging it as a possible issue at the moment

maccamlcQ avatar Dec 17 '19 06:12 maccamlcQ