Looking for working example of AWS.SQS.send_message_batch input parameter shape

I’m looking at the docs for https://hexdocs.pm/aws/AWS.SQS.html#send_message_batch/3

send_message_batch(client, input, options \\ [])

but it’s not clear what the input parameter is. Looking at the aws sdk docs for javascript and golang, I’m inferring that input is something like this in elixir:

input = %{
          "Entries" => batch, #list,
          "QueueUrl" => "..." #string
        }

Where the Entries key should probably be something like a list of maps like this:

 batch = [%{
      "Id" => id,
      "MessageBody" => message_body,
      "MessageDeduplicationId" => group_id,
      "MessageGroupId" => group_id
  }]

The problem is, when I call:

AWS.SQS.send_message_batch(client, input)

I get the error:

** (ArgumentError) cannot convert the given list to a string.

To be converted to a string, a list must either be empty or only
contain the following elements:

  * strings
  * integers representing Unicode code points
  * a list containing one of these three elements

Is this a bug or am I doing something wrong?

It seems as though “Entries” can’t be a list of maps, but I don’t know what else to give it.

The generated code is a really thin wrapper over the underlying API. First thing to notice is how the values in input get encoded for a :query format:

This expects a map with a single level of keys & values, no nesting.

AWS appears to want a special format with .Ns embedded in the field names:

https://sqs.us-east-2.amazonaws.com/123456789012/MyQueue/
?Action=SendMessageBatch
&SendMessageBatchRequestEntry.1.Id=test_msg_001
&SendMessageBatchRequestEntry.1.MessageBody=test%20message%20body%201
&SendMessageBatchRequestEntry.2.Id=test_msg_002
&SendMessageBatchRequestEntry.2.MessageBody=test%20message%20body%202
&SendMessageBatchRequestEntry.2.DelaySeconds=60
&SendMessageBatchRequestEntry.2.MessageAttribute.1.Name=test_attribute_name_1
&SendMessageBatchRequestEntry.2.MessageAttribute.1.Value.StringValue=test_attribute_value_1
&SendMessageBatchRequestEntry.2.MessageAttribute.1.Value.DataType=String
&Expires=2020-05-05T22%3A52%3A43PST
&Version=2012-11-05
&AUTHPARAMS

(from the docs)

Hi @homanchou

input is a list of keywords (but I guess maps would also work) having at least these elements:

[
  id: String.t(),
  message_body: String.t()
]

So make sure you convert your message body to a string, for example using JSON.
See: SendMessageBatchRequestEntry - Amazon Simple Queue Service

This is what my code looks like:

def send_messages(queue, messages) when is_binary(queue) and is_list(messages) do
    messages =
      for {message, id} <- Enum.with_index(messages),
          do: [id: id, message_body: Jason.encode!(message)]

    messages
    |> Enum.chunk_every(@sqs_max_batch_size)
    |> Enum.reduce_while({:ok, 0}, fn batch, {:ok, sent_so_far} ->
      case send_batch(queue, batch) do
        {:ok, sent} -> {:cont, {:ok, sent_so_far + sent}}
        error -> {:halt, error}
      end
    end)
end 

defp send_batch(queue, messages) do
    case ExAws.SQS.send_message_batch(queue, messages) |> ExAws.request() do
      {:ok, %{body: %{failures: [], successes: successes}}} when is_list(successes) ->
        {:ok, length(successes)}

      {:ok, %{body: %{failures: failures}}} ->
        {:error, %{failures: failures}}

      error ->
        error
    end
  end

As you can see, I’m using an integer as message id and that also works.

Hope this can help.