module Aws.Sqs.Commands.Message ( -- * User Message Attributes UserMessageAttributeCustomType , UserMessageAttributeValue(..) , UserMessageAttributeName , UserMessageAttribute -- * Send Message , SendMessage(..) , SendMessageResponse(..) -- * Delete Message , DeleteMessage(..) , DeleteMessageResponse(..) -- * Receive Message , Message(..) , ReceiveMessage(..) , ReceiveMessageResponse(..) -- * Change Message Visiblity , ChangeMessageVisibility(..) , ChangeMessageVisibilityResponse(..) ) where import Aws.Core import Aws.Sqs.Core import Control.Applicative import Control.Monad.Trans.Resource (throwM) import Data.Maybe import Data.Monoid import Text.XML.Cursor (($/), ($//), (&/), (&|)) import qualified Data.ByteString.Base64 as B64 import qualified Data.ByteString.Char8 as B import qualified Data.Text as T import qualified Data.Text.Encoding as TE import Data.Scientific import qualified Network.HTTP.Types as HTTP import Text.Read (readEither) import qualified Text.XML.Cursor as Cu import Prelude -- -------------------------------------------------------------------------- -- -- User Message Attributes -- | You can append a custom type label to the supported data types (String, -- Number, and Binary) to create custom data types. This capability is similar -- to type traits in programming languages. For example, if you have an -- application that needs to know which type of number is being sent in the -- message, then you could create custom types similar to the following: -- Number.byte, Number.short, Number.int, and Number.float. Another example -- using the binary data type is to use Binary.gif and Binary.png to -- distinguish among different image file types in a message or batch of -- messages. The appended data is optional and opaque to Amazon SQS, which -- means that the appended data is not interpreted, validated, or used by -- Amazon SQS. The Custom Type extension has the same restrictions on allowed -- characters as the message body. -- type UserMessageAttributeCustomType = T.Text -- | Message Attribute Value -- -- The user-specified message attribute value. For string data types, the value -- attribute has the same restrictions on the content as the message body. For -- more information, see SendMessage. -- -- Name, type, and value must not be empty or null. In addition, the message -- body should not be empty or null. All parts of the message attribute, -- including name, type, and value, are included in the message size -- restriction, which is currently 256 KB (262,144 bytes). -- -- The supported message attribute data types are String, Number, and Binary. -- You can also provide custom information on the type. The data type has the -- same restrictions on the content as the message body. The data type is case -- sensitive, and it can be up to 256 bytes long. -- -- -- data UserMessageAttributeValue = UserMessageAttributeString (Maybe UserMessageAttributeCustomType) T.Text -- ^ Strings are Unicode with UTF-8 binary encoding. | UserMessageAttributeNumber (Maybe UserMessageAttributeCustomType) Scientific -- ^ Numbers are positive or negative integers or floating point numbers. -- Numbers have sufficient range and precision to encompass most of the -- possible values that integers, floats, and doubles typically support. A -- number can have up to 38 digits of precision, and it can be between -- 10^-128 to 10^+126. Leading and trailing zeroes are trimmed. | UserMessageAttributeBinary (Maybe UserMessageAttributeCustomType) B.ByteString -- ^ Binary type attributes can store any binary data, for example, -- compressed data, encrypted data, or images. -- UserMessageAttributesStringList (Maybe UserMessageAttributeCustomType) [T.Text] -- -- ^ Not implemented. Reserved for future use. -- UserMessageAttributeBinaryList (Maybe UserMessageAttributeCustomType) [B.ByteString] -- -- ^ Not implemented. Reserved for future use. deriving (Show, Read, Eq, Ord) -- | The message attribute name can contain the following characters: A-Z, a-z, -- 0-9, underscore(_), hyphen(-), and period (.). The name must not start or -- end with a period, and it should not have successive periods. The name is -- case sensitive and must be unique among all attribute names for the message. -- The name can be up to 256 characters long. The name cannot start with "AWS." -- or "Amazon." (or any variations in casing) because these prefixes are -- reserved for use by Amazon Web Services. -- type UserMessageAttributeName = T.Text -- | Message Attribute -- -- Name, type, and value must not be empty or null. In addition, the message -- body should not be empty or null. All parts of the message attribute, -- including name, type, and value, are included in the message size -- restriction, which is currently 256 KB (262,144 bytes). -- -- -- -- /NOTE/ -- -- The Amazon SQS API reference calls this /MessageAttribute/. The Haskell -- bindings use this term for what the Amazon documentation calls just -- /Attributes/. In order to limit backward compatibility issues we keep the -- terminology of the Haskell bindings and call this type -- /UserMessageAttributes/. -- type UserMessageAttribute = (UserMessageAttributeName, UserMessageAttributeValue) userMessageAttributesQuery :: [UserMessageAttribute] -> HTTP.Query userMessageAttributesQuery = concat . zipWith msgAttrQuery [1 :: Int ..] where msgAttrQuery i (name, value) = [ ( pre <> "Name", Just $ TE.encodeUtf8 name ) , ( pre <> "Value.DataType", Just typ ) , ( pre <> "Value." <> valueKey, Just encodedValue ) ] where pre = "MessageAttribute." <> B.pack (show i) <> "." customType Nothing t = TE.encodeUtf8 t customType (Just c) t = TE.encodeUtf8 $ t <> "." <> c (typ, valueKey, encodedValue) = case value of UserMessageAttributeString c t -> (customType c "String", "StringValue", TE.encodeUtf8 t) UserMessageAttributeNumber c n -> (customType c "Number", "StringValue", B.pack $ show n) UserMessageAttributeBinary c b -> (customType c "Binary", "BinaryValue", b) -- -------------------------------------------------------------------------- -- -- Send Message -- | Delivers a message to the specified queue. With Amazon SQS, you now have -- the ability to send large payload messages that are up to 256KB (262,144 -- bytes) in size. To send large payloads, you must use an AWS SDK that -- supports SigV4 signing. To verify whether SigV4 is supported for an AWS SDK, -- check the SDK release notes. -- -- /IMPORTANT/ -- -- The following list shows the characters (in Unicode) allowed in your -- message, according to the W3C XML specification. For more information, go to -- If you send any characters not -- included in the list, your request will be rejected. -- -- > #x9 | #xA | #xD | [#x20 to #xD7FF] | [#xE000 to #xFFFD] | [#x10000 to #x10FFFF] -- -- -- data SendMessage = SendMessage { smMessage :: !T.Text -- ^ The message to send. String maximum 256 KB in size. , smQueueName :: !QueueName -- ^ The URL of the Amazon SQS queue to take action on. , smAttributes :: ![UserMessageAttribute] -- ^ Each message attribute consists of a Name, Type, and Value. , smDelaySeconds :: !(Maybe Int) -- ^ The number of seconds (0 to 900 - 15 minutes) to delay a specific -- message. Messages with a positive DelaySeconds value become available for -- processing after the delay time is finished. If you don't specify a value, -- the default value for the queue applies. } deriving (Show, Read, Eq, Ord) -- | At -- -- all fields of @SendMessageResult@ are denoted as optional. -- At -- -- all fields are specified as required. -- -- The actual service seems to treat at least 'smrMD5OfMessageAttributes' -- as optional. -- data SendMessageResponse = SendMessageResponse { smrMD5OfMessageBody :: !T.Text -- ^ An MD5 digest of the non-URL-encoded message body string. This can be -- used to verify that Amazon SQS received the message correctly. Amazon SQS -- first URL decodes the message before creating the MD5 digest. For -- information about MD5, go to . , smrMessageId :: !MessageId -- ^ An element containing the message ID of the message sent to the queue. , smrMD5OfMessageAttributes :: !(Maybe T.Text) -- ^ An MD5 digest of the non-URL-encoded message attribute string. This can -- be used to verify that Amazon SQS received the message correctly. Amazon -- SQS first URL decodes the message before creating the MD5 digest. For -- information about MD5, go to . } deriving (Show, Read, Eq, Ord) instance ResponseConsumer r SendMessageResponse where type ResponseMetadata SendMessageResponse = SqsMetadata responseConsumer _ _ = sqsXmlResponseConsumer parse where parse el = SendMessageResponse <$> force "Missing MD5 Signature" (el $// Cu.laxElement "MD5OfMessageBody" &/ Cu.content) <*> (fmap MessageId . force "Missing Message Id") (el $// Cu.laxElement "MessageId" &/ Cu.content) <*> (pure . listToMaybe) (el $// Cu.laxElement "MD5OfMessageAttributes" &/ Cu.content) instance SignQuery SendMessage where type ServiceConfiguration SendMessage = SqsConfiguration signQuery SendMessage{..} = sqsSignQuery SqsQuery { sqsQueueName = Just smQueueName , sqsQuery = [ ("Action", Just "SendMessage") , ("MessageBody", Just $ TE.encodeUtf8 smMessage) ] <> userMessageAttributesQuery smAttributes <> maybeToList (("DelaySeconds",) . Just . B.pack . show <$> smDelaySeconds) } instance Transaction SendMessage SendMessageResponse instance AsMemoryResponse SendMessageResponse where type MemoryResponse SendMessageResponse = SendMessageResponse loadToMemory = return -- -------------------------------------------------------------------------- -- -- Delete Message -- | Deletes the specified message from the specified queue. You specify the -- message by using the message's receipt handle and not the message ID you -- received when you sent the message. Even if the message is locked by another -- reader due to the visibility timeout setting, it is still deleted from the -- queue. If you leave a message in the queue for longer than the queue's -- configured retention period, Amazon SQS automatically deletes it. -- -- /NOTE/ -- -- The receipt handle is associated with a specific instance of receiving the -- message. If you receive a message more than once, the receipt handle you get -- each time you receive the message is different. When you request -- DeleteMessage, if you don't provide the most recently received receipt -- handle for the message, the request will still succeed, but the message -- might not be deleted. -- -- /IMPORTANT/ -- -- It is possible you will receive a message even after you have deleted it. -- This might happen on rare occasions if one of the servers storing a copy of -- the message is unavailable when you request to delete the message. The copy -- remains on the server and might be returned to you again on a subsequent -- receive request. You should create your system to be idempotent so that -- receiving a particular message more than once is not a problem. -- -- -- data DeleteMessage = DeleteMessage { dmReceiptHandle :: !ReceiptHandle -- ^ The receipt handle associated with the message to delete. , dmQueueName :: !QueueName -- ^ The URL of the Amazon SQS queue to take action on. } deriving (Show, Read, Eq, Ord) data DeleteMessageResponse = DeleteMessageResponse {} deriving (Show, Read, Eq, Ord) instance ResponseConsumer r DeleteMessageResponse where type ResponseMetadata DeleteMessageResponse = SqsMetadata responseConsumer _ _ = sqsXmlResponseConsumer parse where parse _ = return DeleteMessageResponse {} instance SignQuery DeleteMessage where type ServiceConfiguration DeleteMessage = SqsConfiguration signQuery DeleteMessage{..} = sqsSignQuery SqsQuery { sqsQueueName = Just dmQueueName , sqsQuery = [ ("Action", Just "DeleteMessage") , ("ReceiptHandle", Just $ TE.encodeUtf8 $ printReceiptHandle dmReceiptHandle) ] } instance Transaction DeleteMessage DeleteMessageResponse instance AsMemoryResponse DeleteMessageResponse where type MemoryResponse DeleteMessageResponse = DeleteMessageResponse loadToMemory = return -- -------------------------------------------------------------------------- -- -- Receive Message -- | Retrieves one or more messages, with a maximum limit of 10 messages, from -- the specified queue. Long poll support is enabled by using the -- WaitTimeSeconds parameter. For more information, see -- -- in the Amazon SQS Developer Guide. -- -- Short poll is the default behavior where a weighted random set of machines -- is sampled on a ReceiveMessage call. This means only the messages on the -- sampled machines are returned. If the number of messages in the queue is -- small (less than 1000), it is likely you will get fewer messages than you -- requested per ReceiveMessage call. If the number of messages in the queue is -- extremely small, you might not receive any messages in a particular -- ReceiveMessage response; in which case you should repeat the request. -- -- For each message returned, the response includes the following: -- -- Message body -- -- * MD5 digest of the message body. For information about MD5, go to -- . -- -- * Message ID you received when you sent the message to the queue. -- -- * Receipt handle. -- -- * Message attributes. -- -- * MD5 digest of the message attributes. -- -- The receipt handle is the identifier you must provide when deleting the -- message. For more information, see Queue and Message Identifiers in the -- Amazon SQS Developer Guide. -- -- You can provide the VisibilityTimeout parameter in your request, which will -- be applied to the messages that Amazon SQS returns in the response. If you -- do not include the parameter, the overall visibility timeout for the queue -- is used for the returned messages. For more information, see Visibility -- Timeout in the Amazon SQS Developer Guide. -- -- /NOTE/ -- -- Going forward, new attributes might be added. If you are writing code that -- calls this action, we recommend that you structure your code so that it can -- handle new attributes gracefully. -- -- -- data ReceiveMessage = ReceiveMessage { rmVisibilityTimeout :: !(Maybe Int) -- ^ The duration (in seconds) that the received messages are hidden from -- subsequent retrieve requests after being retrieved by a ReceiveMessage -- request. , rmAttributes :: ![MessageAttribute] -- ^ A list of attributes that need to be returned along with each message. -- -- The following lists the names and descriptions of the attributes that can -- be returned: -- -- * All - returns all values. -- -- * ApproximateFirstReceiveTimestamp - returns the time when the message was -- first received (epoch time in milliseconds). -- -- * ApproximateReceiveCount - returns the number of times a message has been -- received but not deleted. -- -- * SenderId - returns the AWS account number (or the IP address, if -- anonymous access is allowed) of the sender. -- -- * SentTimestamp - returns the time when the message was sent (epoch time -- in milliseconds). , rmMaxNumberOfMessages :: !(Maybe Int) -- ^ The maximum number of messages to return. Amazon SQS never returns more -- messages than this value but may return fewer. Values can be from 1 to 10. -- Default is 1. -- -- All of the messages are not necessarily returned. , rmUserMessageAttributes :: ![UserMessageAttributeName] -- ^ The name of the message attribute, where N is the index. The message -- attribute name can contain the following characters: A-Z, a-z, 0-9, -- underscore (_), hyphen (-), and period (.). The name must not start or end -- with a period, and it should not have successive periods. The name is case -- sensitive and must be unique among all attribute names for the message. -- The name can be up to 256 characters long. The name cannot start with -- "AWS." or "Amazon." (or any variations in casing), because these prefixes -- are reserved for use by Amazon Web Services. -- -- When using ReceiveMessage, you can send a list of attribute names to -- receive, or you can return all of the attributes by specifying "All" or -- ".*" in your request. You can also use "foo.*" to return all message -- attributes starting with the "foo" prefix. , rmQueueName :: !QueueName -- ^The URL of the Amazon SQS queue to take action on. , rmWaitTimeSeconds :: !(Maybe Int) -- ^ The duration (in seconds) for which the call will wait for a message to -- arrive in the queue before returning. If a message is available, the call -- will return sooner than WaitTimeSeconds. } deriving (Show, Read, Eq, Ord) -- | An Amazon SQS message. -- -- In -- -- all elements are denoted as optional. -- In -- -- all elements except for the attributes are specified as required. -- At least for the field 'mMD5OfMessageAttributes' the the service -- is not always returning a value and therefor we make this field optional. -- data Message = Message { mMessageId :: !T.Text -- ^ A unique identifier for the message. Message IDs are considered unique -- across all AWS accounts for an extended period of time. , mReceiptHandle :: !ReceiptHandle -- ^ An identifier associated with the act of receiving the message. A new -- receipt handle is returned every time you receive a message. When deleting -- a message, you provide the last received receipt handle to delete the -- message. , mMD5OfBody :: !T.Text -- ^ An MD5 digest of the non-URL-encoded message body string. , mBody :: T.Text -- ^ The message's contents (not URL-encoded). , mAttributes :: ![(MessageAttribute,T.Text)] -- ^ SenderId, SentTimestamp, ApproximateReceiveCount, and/or -- ApproximateFirstReceiveTimestamp. SentTimestamp and -- ApproximateFirstReceiveTimestamp are each returned as an integer -- representing the epoch time in milliseconds. , mMD5OfMessageAttributes :: !(Maybe T.Text) -- ^ An MD5 digest of the non-URL-encoded message attribute string. This can -- be used to verify that Amazon SQS received the message correctly. Amazon -- SQS first URL decodes the message before creating the MD5 digest. For -- information about MD5, go to . , mUserMessageAttributes :: ![UserMessageAttribute] -- ^ Each message attribute consists of a Name, Type, and Value. } deriving(Show, Read, Eq, Ord) data ReceiveMessageResponse = ReceiveMessageResponse { rmrMessages :: ![Message] } deriving (Show, Read, Eq, Ord) readMessageAttribute :: Cu.Cursor -> Response SqsMetadata (MessageAttribute,T.Text) readMessageAttribute cursor = do name <- force "Missing Name" $ cursor $/ Cu.laxElement "Name" &/ Cu.content value <- force "Missing Value" $ cursor $/ Cu.laxElement "Value" &/ Cu.content parsedName <- parseMessageAttribute name return (parsedName, value) readUserMessageAttribute :: Cu.Cursor -> Response SqsMetadata UserMessageAttribute readUserMessageAttribute cursor = (,) <$> force "Missing Name" (cursor $/ Cu.laxElement "Name" &/ Cu.content) <*> readUserMessageAttributeValue cursor readUserMessageAttributeValue :: Cu.Cursor -> Response SqsMetadata UserMessageAttributeValue readUserMessageAttributeValue cursor = do typStr <- force "Missing DataType" $ cursor $// Cu.laxElement "DataType" &/ Cu.content case parseType typStr of ("String", c) -> do val <- force "Missing StringValue" $ cursor $// Cu.laxElement "StringValue" &/ Cu.content return $ UserMessageAttributeString c val ("Number", c) -> do valStr <- force "Missing StringValue" $ cursor $// Cu.laxElement "StringValue" &/ Cu.content val <- tryXml . readEither $ T.unpack valStr return $ UserMessageAttributeNumber c val ("Binary", c) -> do val64 <- force "Missing BinaryValue" $ cursor $// Cu.laxElement "BinaryValue" &/ Cu.content val <- tryXml . B64.decode $ TE.encodeUtf8 val64 return $ UserMessageAttributeBinary c val (x, _) -> throwM . XmlException $ "unkown data type for MessageAttributeValue: " <> T.unpack x where parseType s = case T.break (== '.') s of (a, "") -> (a, Nothing) (a, x) -> (a, Just (T.tail x)) tryXml = either (throwM . XmlException) return readMessage :: Cu.Cursor -> Response SqsMetadata Message readMessage cursor = do mid <- force "Missing Message Id" $ cursor $// Cu.laxElement "MessageId" &/ Cu.content rh <- force "Missing Reciept Handle" $ cursor $// Cu.laxElement "ReceiptHandle" &/ Cu.content md5 <- force "Missing MD5 Signature" $ cursor $// Cu.laxElement "MD5OfBody" &/ Cu.content body <- force "Missing Body" $ cursor $// Cu.laxElement "Body" &/ Cu.content attributes <- sequence $ cursor $// Cu.laxElement "Attribute" &| readMessageAttribute userAttributes <- sequence $ cursor $// Cu.laxElement "MessageAttribute" &| readUserMessageAttribute let md5OfMessageAttributes = listToMaybe $ cursor $// Cu.laxElement "MD5OfMessageAttributes" &/ Cu.content return Message { mMessageId = mid , mReceiptHandle = ReceiptHandle rh , mMD5OfBody = md5 , mBody = body , mAttributes = attributes , mMD5OfMessageAttributes = md5OfMessageAttributes , mUserMessageAttributes = userAttributes } formatMAttributes :: [MessageAttribute] -> HTTP.Query formatMAttributes attrs = case attrs of [attr] -> [("AttributeName", encodeAttr attr)] _ -> zipWith f [1 :: Int ..] attrs where f x y = ("AttributeName." <> B.pack (show x), encodeAttr y) encodeAttr = Just . TE.encodeUtf8 . printMessageAttribute formatUserMessageAttributes :: [UserMessageAttributeName] -> HTTP.Query formatUserMessageAttributes attrs = case attrs of [attr] -> [("MessageAttributeName", encodeAttr attr)] _ -> zipWith f [1 :: Int ..] attrs where f x y = ("MessageAttributeName." <> B.pack (show x), encodeAttr y) encodeAttr = Just . TE.encodeUtf8 instance ResponseConsumer r ReceiveMessageResponse where type ResponseMetadata ReceiveMessageResponse = SqsMetadata responseConsumer _ _ = sqsXmlResponseConsumer parse where parse el = do result <- force "Missing ReceiveMessageResult" $ el $// Cu.laxElement "ReceiveMessageResult" messages <- sequence $ result $// Cu.laxElement "Message" &| readMessage return ReceiveMessageResponse{ rmrMessages = messages } instance SignQuery ReceiveMessage where type ServiceConfiguration ReceiveMessage = SqsConfiguration signQuery ReceiveMessage{..} = sqsSignQuery SqsQuery { sqsQueueName = Just rmQueueName , sqsQuery = [ ("Action", Just "ReceiveMessage") ] <> catMaybes [ ("VisibilityTimeout",) <$> case rmVisibilityTimeout of Just x -> Just $ Just $ B.pack $ show x Nothing -> Nothing , ("MaxNumberOfMessages",) <$> case rmMaxNumberOfMessages of Just x -> Just $ Just $ B.pack $ show x Nothing -> Nothing , ("WaitTimeSeconds",) <$> case rmWaitTimeSeconds of Just x -> Just $ Just $ B.pack $ show x Nothing -> Nothing ] <> formatMAttributes rmAttributes <> formatUserMessageAttributes rmUserMessageAttributes } instance Transaction ReceiveMessage ReceiveMessageResponse instance AsMemoryResponse ReceiveMessageResponse where type MemoryResponse ReceiveMessageResponse = ReceiveMessageResponse loadToMemory = return -- -------------------------------------------------------------------------- -- -- Change Message Visibility -- | Changes the visibility timeout of a specified message in a queue to a new -- value. The maximum allowed timeout value you can set the value to is 12 -- hours. This means you can't extend the timeout of a message in an existing -- queue to more than a total visibility timeout of 12 hours. (For more -- information visibility timeout, see Visibility Timeout in the Amazon SQS -- Developer Guide.) -- -- For example, let's say you have a message and its default message visibility -- timeout is 30 minutes. You could call ChangeMessageVisiblity with a value of -- two hours and the effective timeout would be two hours and 30 minutes. When -- that time comes near you could again extend the time out by calling -- ChangeMessageVisiblity, but this time the maximum allowed timeout would be 9 -- hours and 30 minutes. -- -- /NOTE/ -- -- There is a 120,000 limit for the number of inflight messages per queue. -- Messages are inflight after they have been received from the queue by a -- consuming component, but have not yet been deleted from the queue. If you -- reach the 120,000 limit, you will receive an OverLimit error message from -- Amazon SQS. To help avoid reaching the limit, you should delete the messages -- from the queue after they have been processed. You can also increase the -- number of queues you use to process the messages. -- -- /IMPORTANT/ -- -- If you attempt to set the VisibilityTimeout to an amount more than the -- maximum time left, Amazon SQS returns an error. It will not automatically -- recalculate and increase the timeout to the maximum time remaining. -- -- /IMPORTANT/ -- -- Unlike with a queue, when you change the visibility timeout for a specific -- message, that timeout value is applied immediately but is not saved in -- memory for that message. If you don't delete a message after it is received, -- the visibility timeout for the message the next time it is received reverts -- to the original timeout value, not the value you set with the -- ChangeMessageVisibility action. -- -- -- data ChangeMessageVisibility = ChangeMessageVisibility { cmvReceiptHandle :: !ReceiptHandle -- ^ The receipt handle associated with the message whose visibility timeout -- should be changed. This parameter is returned by the ReceiveMessage -- action. , cmvVisibilityTimeout :: !Int -- ^ The new value (in seconds - from 0 to 43200 - maximum 12 hours) for the -- message's visibility timeout. , cmvQueueName :: !QueueName -- ^ The URL of the Amazon SQS queue to take action on. } deriving (Show, Read, Eq, Ord) data ChangeMessageVisibilityResponse = ChangeMessageVisibilityResponse {} deriving (Show, Read, Eq, Ord) instance ResponseConsumer r ChangeMessageVisibilityResponse where type ResponseMetadata ChangeMessageVisibilityResponse = SqsMetadata responseConsumer _ _ = sqsXmlResponseConsumer parse where parse _ = return ChangeMessageVisibilityResponse {} -- | ServiceConfiguration: 'SqsConfiguration' instance SignQuery ChangeMessageVisibility where type ServiceConfiguration ChangeMessageVisibility = SqsConfiguration signQuery ChangeMessageVisibility {..} = sqsSignQuery SqsQuery { sqsQueueName = Just cmvQueueName , sqsQuery = [ ("Action", Just "ChangeMessageVisibility") , ("ReceiptHandle", Just . TE.encodeUtf8 $ printReceiptHandle cmvReceiptHandle) , ("VisibilityTimeout", Just . B.pack $ show cmvVisibilityTimeout) ] } instance Transaction ChangeMessageVisibility ChangeMessageVisibilityResponse instance AsMemoryResponse ChangeMessageVisibilityResponse where type MemoryResponse ChangeMessageVisibilityResponse = ChangeMessageVisibilityResponse loadToMemory = return