aws-sdk-resources 2.11.549

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/lib/aws-sdk-resources.rb +91 -0
  3. data/lib/aws-sdk-resources/batch.rb +143 -0
  4. data/lib/aws-sdk-resources/builder.rb +85 -0
  5. data/lib/aws-sdk-resources/builder_sources.rb +105 -0
  6. data/lib/aws-sdk-resources/collection.rb +107 -0
  7. data/lib/aws-sdk-resources/definition.rb +331 -0
  8. data/lib/aws-sdk-resources/documenter.rb +70 -0
  9. data/lib/aws-sdk-resources/documenter/base_operation_documenter.rb +279 -0
  10. data/lib/aws-sdk-resources/documenter/data_operation_documenter.rb +25 -0
  11. data/lib/aws-sdk-resources/documenter/has_many_operation_documenter.rb +69 -0
  12. data/lib/aws-sdk-resources/documenter/has_operation_documenter.rb +66 -0
  13. data/lib/aws-sdk-resources/documenter/operation_documenter.rb +20 -0
  14. data/lib/aws-sdk-resources/documenter/resource_operation_documenter.rb +53 -0
  15. data/lib/aws-sdk-resources/documenter/waiter_operation_documenter.rb +77 -0
  16. data/lib/aws-sdk-resources/errors.rb +15 -0
  17. data/lib/aws-sdk-resources/operation_methods.rb +83 -0
  18. data/lib/aws-sdk-resources/operations.rb +280 -0
  19. data/lib/aws-sdk-resources/options.rb +17 -0
  20. data/lib/aws-sdk-resources/request.rb +39 -0
  21. data/lib/aws-sdk-resources/request_params.rb +140 -0
  22. data/lib/aws-sdk-resources/resource.rb +243 -0
  23. data/lib/aws-sdk-resources/services/ec2.rb +21 -0
  24. data/lib/aws-sdk-resources/services/ec2/instance.rb +29 -0
  25. data/lib/aws-sdk-resources/services/iam.rb +19 -0
  26. data/lib/aws-sdk-resources/services/s3.rb +20 -0
  27. data/lib/aws-sdk-resources/services/s3/bucket.rb +131 -0
  28. data/lib/aws-sdk-resources/services/s3/encryption.rb +21 -0
  29. data/lib/aws-sdk-resources/services/s3/encryption/client.rb +369 -0
  30. data/lib/aws-sdk-resources/services/s3/encryption/decrypt_handler.rb +174 -0
  31. data/lib/aws-sdk-resources/services/s3/encryption/default_cipher_provider.rb +63 -0
  32. data/lib/aws-sdk-resources/services/s3/encryption/default_key_provider.rb +38 -0
  33. data/lib/aws-sdk-resources/services/s3/encryption/encrypt_handler.rb +50 -0
  34. data/lib/aws-sdk-resources/services/s3/encryption/errors.rb +13 -0
  35. data/lib/aws-sdk-resources/services/s3/encryption/io_auth_decrypter.rb +56 -0
  36. data/lib/aws-sdk-resources/services/s3/encryption/io_decrypter.rb +29 -0
  37. data/lib/aws-sdk-resources/services/s3/encryption/io_encrypter.rb +69 -0
  38. data/lib/aws-sdk-resources/services/s3/encryption/key_provider.rb +29 -0
  39. data/lib/aws-sdk-resources/services/s3/encryption/kms_cipher_provider.rb +71 -0
  40. data/lib/aws-sdk-resources/services/s3/encryption/materials.rb +58 -0
  41. data/lib/aws-sdk-resources/services/s3/encryption/utils.rb +79 -0
  42. data/lib/aws-sdk-resources/services/s3/file_downloader.rb +169 -0
  43. data/lib/aws-sdk-resources/services/s3/file_part.rb +75 -0
  44. data/lib/aws-sdk-resources/services/s3/file_uploader.rb +58 -0
  45. data/lib/aws-sdk-resources/services/s3/multipart_file_uploader.rb +187 -0
  46. data/lib/aws-sdk-resources/services/s3/multipart_upload.rb +42 -0
  47. data/lib/aws-sdk-resources/services/s3/multipart_upload_error.rb +16 -0
  48. data/lib/aws-sdk-resources/services/s3/object.rb +290 -0
  49. data/lib/aws-sdk-resources/services/s3/object_copier.rb +99 -0
  50. data/lib/aws-sdk-resources/services/s3/object_multipart_copier.rb +180 -0
  51. data/lib/aws-sdk-resources/services/s3/object_summary.rb +73 -0
  52. data/lib/aws-sdk-resources/services/s3/presigned_post.rb +651 -0
  53. data/lib/aws-sdk-resources/services/sns.rb +7 -0
  54. data/lib/aws-sdk-resources/services/sns/message_verifier.rb +171 -0
  55. data/lib/aws-sdk-resources/services/sqs.rb +7 -0
  56. data/lib/aws-sdk-resources/services/sqs/queue_poller.rb +521 -0
  57. data/lib/aws-sdk-resources/source.rb +39 -0
  58. metadata +118 -0
@@ -0,0 +1,7 @@
1
+ module Aws
2
+ module SNS
3
+
4
+ autoload :MessageVerifier, 'aws-sdk-resources/services/sns/message_verifier'
5
+
6
+ end
7
+ end
@@ -0,0 +1,171 @@
1
+ require 'net/http'
2
+ require 'openssl'
3
+ require 'base64'
4
+
5
+ module Aws
6
+ module SNS
7
+
8
+ # A utility class that can be used to verify the authenticity of messages
9
+ # sent by Amazon SNS.
10
+ #
11
+ # verifier = Aws::SNS::MessageVerifier.new
12
+ #
13
+ # # returns true/false
14
+ # verifier.authentic?(message_body)
15
+ #
16
+ # # raises a Aws::SNS::MessageVerifier::VerificationError on failure
17
+ # verifier.authenticate!(message_body)
18
+ #
19
+ # You can re-use a single {MessageVerifier} instance to authenticate
20
+ # multiple SNS messages.
21
+ class MessageVerifier
22
+
23
+ class VerificationError < StandardError; end
24
+
25
+ # @api private
26
+ SIGNABLE_KEYS = [
27
+ 'Message',
28
+ 'MessageId',
29
+ 'Subject',
30
+ 'SubscribeURL',
31
+ 'Timestamp',
32
+ 'Token',
33
+ 'TopicArn',
34
+ 'Type',
35
+ ].freeze
36
+
37
+ # @api private
38
+ AWS_HOSTNAMES = [
39
+ /^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/
40
+ ]
41
+
42
+ def initialize
43
+ @cached_pems = {}
44
+ end
45
+
46
+ # @param [String<JSON>] message_body
47
+ # @return [Boolean] Returns `true` if the given message has been
48
+ # successfully verified. Returns `false` otherwise.
49
+ def authentic?(message_body)
50
+ authenticate!(message_body)
51
+ rescue VerificationError
52
+ false
53
+ end
54
+
55
+ # @param [String<JSON>] message_body
56
+ # @return [Boolean] Returns `true` when the given message has been
57
+ # successfully verified.
58
+ # @raise [VerificationError] Raised when the given message has failed
59
+ # verification.
60
+ def authenticate!(message_body)
61
+ msg = Json.load(message_body)
62
+ msg = convert_lambda_msg(msg) if is_from_lambda(msg)
63
+ if public_key(msg).verify(sha1, signature(msg), canonical_string(msg))
64
+ true
65
+ else
66
+ msg = 'the authenticity of the message cannot be verified'
67
+ raise VerificationError, msg
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def is_from_lambda(message)
74
+ message.key? 'SigningCertUrl'
75
+ end
76
+
77
+ def convert_lambda_msg(message)
78
+ cert_url = message.delete('SigningCertUrl')
79
+ unsubscribe_url = message.delete('UnsubscribeUrl')
80
+
81
+ message['SigningCertURL'] = cert_url
82
+ message['UnsubscribeURL'] = unsubscribe_url
83
+ message
84
+ end
85
+
86
+ def sha1
87
+ OpenSSL::Digest::SHA1.new
88
+ end
89
+
90
+ def signature(message)
91
+ Base64.decode64(message['Signature'])
92
+ end
93
+
94
+ def canonical_string(message)
95
+ parts = []
96
+ SIGNABLE_KEYS.each do |key|
97
+ value = message[key]
98
+ unless value.nil? or value.empty?
99
+ parts << "#{key}\n#{value}\n"
100
+ end
101
+ end
102
+ parts.join
103
+ end
104
+
105
+ def public_key(message)
106
+ x509_url = URI.parse(message['SigningCertURL'])
107
+ x509 = OpenSSL::X509::Certificate.new(pem(x509_url))
108
+ OpenSSL::PKey::RSA.new(x509.public_key)
109
+ end
110
+
111
+ def pem(uri)
112
+ if @cached_pems[uri.to_s]
113
+ @cached_pems[uri.to_s]
114
+ else
115
+ @cached_pems[uri.to_s] = download_pem(uri)
116
+ end
117
+ end
118
+
119
+ def download_pem(uri)
120
+ verify_uri!(uri)
121
+ https_get(uri)
122
+ end
123
+
124
+ def verify_uri!(uri)
125
+ verify_https!(uri)
126
+ verify_hosted_by_aws!(uri)
127
+ verify_pem!(uri)
128
+ end
129
+
130
+ def verify_https!(uri)
131
+ unless uri.scheme == 'https'
132
+ msg = "the SigningCertURL must be https, got: #{uri}"
133
+ raise VerificationError, msg
134
+ end
135
+ end
136
+
137
+ def verify_hosted_by_aws!(uri)
138
+ unless AWS_HOSTNAMES.any? { |pattern| pattern.match(uri.host) }
139
+ msg = "signing cert is not hosted by AWS: #{uri}"
140
+ raise VerificationError, msg
141
+ end
142
+ end
143
+
144
+ def verify_pem!(uri)
145
+ unless File.extname(uri.path) == '.pem'
146
+ msg = "the SigningCertURL must link to a .pem file"
147
+ raise VerificationError, msg
148
+ end
149
+ end
150
+
151
+ def https_get(uri, failed_attempts = 0)
152
+ http = Net::HTTP.new(uri.host, uri.port)
153
+ http.use_ssl = true
154
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
155
+ http.start
156
+ resp = http.request(Net::HTTP::Get.new(uri.request_uri))
157
+ http.finish
158
+ if resp.code == '200'
159
+ resp.body
160
+ else
161
+ raise VerificationError, resp.body
162
+ end
163
+ rescue => error
164
+ failed_attempts += 1
165
+ retry if failed_attempts < 3
166
+ raise VerificationError, error.message
167
+ end
168
+
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,7 @@
1
+ module Aws
2
+ module SQS
3
+
4
+ autoload :QueuePoller, 'aws-sdk-resources/services/sqs/queue_poller'
5
+
6
+ end
7
+ end
@@ -0,0 +1,521 @@
1
+ require 'set'
2
+
3
+ module Aws
4
+ module SQS
5
+
6
+ # A utility class for long polling messages in a loop. **Messages are
7
+ # automatically deleted from the queue at the end of the given block.**
8
+ #
9
+ # poller = Aws::SQS::QueuePoller.new(queue_url)
10
+ #
11
+ # poller.poll do |msg|
12
+ # puts msg.body
13
+ # end
14
+ #
15
+ # ## Long Polling
16
+ #
17
+ # By default, messages are received using long polling. This
18
+ # method will force a default `:wait_time_seconds` of 20 seconds.
19
+ # If you prefer to use the queue default wait time, then pass
20
+ # a `nil` value for `:wait_time_seconds`.
21
+ #
22
+ # # disables 20 second default, use queue ReceiveMessageWaitTimeSeconds
23
+ # poller.poll(wait_time_seconds:nil) do |msg|
24
+ # # ...
25
+ # end
26
+ #
27
+ # When disabling `:wait_time_seconds` by passing `nil`, you must
28
+ # ensure the queue `ReceiveMessageWaitTimeSeconds` attribute is
29
+ # set to a non-zero value, or you will be short-polling.
30
+ # This will trigger significantly more API calls.
31
+ #
32
+ # ## Batch Receiving Messages
33
+ #
34
+ # You can specify a maximum number of messages to receive with
35
+ # each polling attempt via `:max_number_of_messages`. When this is
36
+ # set to a positive value, greater than 1, the block will receive
37
+ # an array of messages, instead of a single message.
38
+ #
39
+ # # receives and yields 1 message at a time
40
+ # poller.poll do |msg|
41
+ # # ...
42
+ # end
43
+ #
44
+ # # receives and yields up to 10 messages at a time
45
+ # poller.poll(max_number_of_messages:10) do |messages|
46
+ # messages.each do |msg|
47
+ # # ...
48
+ # end
49
+ # end
50
+ #
51
+ # The maximum value for `:max_number_of_messages` is enforced by
52
+ # Amazon SQS.
53
+ #
54
+ # ## Visibility Timeouts
55
+ #
56
+ # When receiving messages, you have a fixed amount of time to process
57
+ # and delete the message before it is added back into the queue. This
58
+ # is the visibility timeout. By default, the queue's `VisibilityTimeout`
59
+ # attribute is used. You can provide an alternative visibility timeout
60
+ # when polling.
61
+ #
62
+ # # queue default VisibilityTimeout
63
+ # poller.poll do |msg|
64
+ # end
65
+ #
66
+ # # custom visibility timeout
67
+ # poller.poll(visibility_timeout:10) do |msg|
68
+ # end
69
+ #
70
+ #
71
+ # You can reset the visibility timeout of a single message by calling
72
+ # {#change_message_visibility_timeout}. This is useful when you need
73
+ # more time to finish processing the message.
74
+ #
75
+ # poller.poll do |msg|
76
+ #
77
+ # # do work ...
78
+ #
79
+ # # need more time for processing
80
+ # poller.change_message_visibility_timeout(msg, 60)
81
+ #
82
+ # # finish work ...
83
+ #
84
+ # end
85
+ #
86
+ # If you change the visibility timeout of a message to zero, it will
87
+ # return to the queue immediately.
88
+ #
89
+ # ## Deleting Messages
90
+ #
91
+ # Messages are deleted from the queue when the block returns normally.
92
+ #
93
+ # poller.poll do |msg|
94
+ # # do work
95
+ # end # messages deleted here
96
+ #
97
+ # You can skip message deletion by passing `skip_delete: true`.
98
+ # This allows you to manually delete the messages using
99
+ # {#delete_message}, or {#delete_messages}.
100
+ #
101
+ # # single message
102
+ # poller.poll(skip_delete: true) do |msg|
103
+ # poller.delete_message(msg) # if successful
104
+ # end
105
+ #
106
+ # # batch delete messages
107
+ # poller.poll(skip_delete: true, max_number_of_messages:10) do |messages|
108
+ # poller.delete_messages(messages)
109
+ # end
110
+ #
111
+ # Another way to manage message deletion is to throw `:skip_delete`
112
+ # from the poll block. You can use this to choose when a message, or
113
+ # message batch is deleted on an individual basis. This can be very
114
+ # useful when you are capturing temporal errors and wish for the
115
+ # message to timeout.
116
+ #
117
+ # poller.poll do |msg|
118
+ # begin
119
+ # # do work
120
+ # rescue
121
+ # # unexpected error occurred while processing messages,
122
+ # # log it, and skip delete so it can be re-processed later
123
+ # throw :skip_delete
124
+ # end
125
+ # end
126
+ #
127
+ # ## Terminating the Polling Loop
128
+ #
129
+ # By default, polling will continue indefinitely. You can stop
130
+ # the poller by providing an idle timeout or by throwing `:stop_polling`
131
+ # from the {#before_request} callback.
132
+ #
133
+ # ### `:idle_timeout` Option
134
+ #
135
+ # This is a configurable, maximum number of seconds to wait for a
136
+ # new message before the polling loop exists. By default, there is
137
+ # no idle timeout.
138
+ #
139
+ # # stops polling after a minute of no received messages
140
+ # poller.poll(idle_timeout: 60) do |msg|
141
+ # # ...
142
+ # end
143
+ #
144
+ # ### Throw `:stop_polling`
145
+ #
146
+ # If you want more fine grained control, you can configure a
147
+ # before request callback to trigger before each long poll. Throwing
148
+ # `:stop_polling` from this callback will cause the poller to exit
149
+ # normally without making the next request.
150
+ #
151
+ # # stop after processing 100 messages
152
+ # poller.before_request do |stats|
153
+ # throw :stop_polling if stats.received_message_count >= 100
154
+ # end
155
+ #
156
+ # poller.poll do |msg|
157
+ # # do work ...
158
+ # end
159
+ #
160
+ # ## Tracking Progress
161
+ #
162
+ # The poller will automatically track a few statistics client-side in
163
+ # a {PollerStats} object. You can access the poller stats
164
+ # three ways:
165
+ #
166
+ # * The first block argument of {#before_request}
167
+ # * The second block argument of {#poll}.
168
+ # * The return value from {#poll}.
169
+ #
170
+ # Here are examples of accessing the statistics.
171
+ #
172
+ # * Configure a {#before_request} callback.
173
+ #
174
+ # ```
175
+ # poller.before_request do |stats|
176
+ # logger.info("requests: #{stats.request_count}")
177
+ # logger.info("messages: #{stats.received_message_count}")
178
+ # logger.info("last-timestamp: #{stats.last_message_received_at}")
179
+ # end
180
+ # ```
181
+ #
182
+ # * Accept a 2nd argument in the poll block, for example:
183
+ #
184
+ # ```
185
+ # poller.poll do |msg, stats|
186
+ # logger.info("requests: #{stats.request_count}")
187
+ # logger.info("messages: #{stats.received_message_count}")
188
+ # logger.info("last-timestamp: #{stats.last_message_received_at}")
189
+ # end
190
+ # ```
191
+ #
192
+ # * Return value:
193
+ #
194
+ # ```
195
+ # stats = poller.poll(idle_timeout:10) do |msg|
196
+ # # do work ...
197
+ # end
198
+ # logger.info("requests: #{stats.request_count}")
199
+ # logger.info("messages: #{stats.received_message_count}")
200
+ # logger.info("last-timestamp: #{stats.last_message_received_at}")
201
+ # ```
202
+ #
203
+ class QueuePoller
204
+
205
+ # @param [String] queue_url
206
+ # @option options [Client] :client
207
+ # @option (see #poll)
208
+ def initialize(queue_url, options = {})
209
+ @queue_url = queue_url
210
+ @client = options.delete(:client) || Client.new
211
+ @default_config = PollerConfig.new(options)
212
+ end
213
+
214
+ # @return [String]
215
+ attr_reader :queue_url
216
+
217
+ # @return [Client]
218
+ attr_reader :client
219
+
220
+ # @return [PollerConfig]
221
+ attr_reader :default_config
222
+
223
+ # Registers a callback that is invoked once before every polling
224
+ # attempt.
225
+ #
226
+ # poller.before_request do |stats|
227
+ # logger.info("requests: #{stats.request_count}")
228
+ # logger.info("messages: #{stats.received_message_count}")
229
+ # logger.info("last-timestamp: #{stats.last_message_received_at}")
230
+ # end
231
+ #
232
+ # poller.poll do |msg|
233
+ # # do work ...
234
+ # end
235
+ #
236
+ # ## `:stop_polling`
237
+ #
238
+ # If you throw `:stop_polling` from the {#before_request} callback,
239
+ # then the poller will exit normally before making the next long
240
+ # poll request.
241
+ #
242
+ # poller.before_request do |stats|
243
+ # throw :stop_polling if stats.received_messages >= 100
244
+ # end
245
+ #
246
+ # # at most 100 messages will be yielded
247
+ # poller.poll do |msg|
248
+ # # do work ...
249
+ # end
250
+ #
251
+ # @yieldparam [PollerStats] stats An object that tracks a few
252
+ # client-side statistics about the queue polling.
253
+ #
254
+ # @return [void]
255
+ def before_request(&block)
256
+ @default_config = @default_config.with(before_request: block) if block_given?
257
+ end
258
+
259
+ # Polls the queue, yielded a message, or an array of messages.
260
+ # Messages are automatically deleted from the queue at the
261
+ # end of the given block. See the class documentation on
262
+ # {QueuePoller} for more examples.
263
+ #
264
+ # @example Basic example, loops indefinitely
265
+ #
266
+ # poller.poll do |msg|
267
+ # # ...
268
+ # end
269
+ #
270
+ # @example Receives and deletes messages as a batch
271
+ #
272
+ # poller.poll(max_number_of_messages:10) do |messages|
273
+ # messages.each do |msg|
274
+ # # ...
275
+ # end
276
+ # end
277
+ #
278
+ # @option options [Integer] :wait_time_seconds (20) The
279
+ # long polling interval. Messages are yielded as soon as they are
280
+ # received. The `:wait_time_seconds` option specifies the max
281
+ # duration for each polling attempt before a new request is
282
+ # sent to receive messages.
283
+ #
284
+ # @option options [Integer] :max_number_of_messages (1) The maximum
285
+ # number of messages to yield from each polling attempt.
286
+ # Values can be from 1 to 10.
287
+ #
288
+ # @option options [Integer] :visibility_timeout (nil)
289
+ # The number of seconds you have to process a message before
290
+ # it is put back into the queue and can be received again.
291
+ # By default, the queue's
292
+ #
293
+ # @option options [Array<String>] :attribute_names ([])
294
+ # The list of attributes that need to be returned along with each
295
+ # message. Valid attribute names include:
296
+ #
297
+ # * `All` - All attributes.
298
+ # * `ApproximateFirstReceiveTimestamp` - The time when the message
299
+ # was first received from the queue (epoch time in milliseconds).
300
+ # * `ApproximateReceiveCount` - The number of times a message has
301
+ # been received from the queue but not deleted.
302
+ # * `SenderId` - The AWS account number (or the IP address, if
303
+ # anonymous access is allowed) of the sender.
304
+ # * `SentTimestamp` - The time when the message was sent to the
305
+ # queue (epoch time in milliseconds).
306
+ #
307
+ # @option options [Array<String>] :message_attribute_names ([])
308
+ # A list of message attributes to receive. You can receive
309
+ # all messages by using `All` or `.*`. You can also use
310
+ # `foo.*` to return all message attributes starting with the
311
+ # `foo` prefix.
312
+ #
313
+ # @option options [Integer] :idle_timeout (nil) Polling terminates
314
+ # gracefully when `:idle_timeout` seconds have passed without
315
+ # receiving any messages.
316
+ #
317
+ # @option options [Boolean] :skip_delete (false) When `true`, messages
318
+ # are not deleted after polling block. If you wish to delete
319
+ # received messages, you will need to call `#delete_message` or
320
+ # `#delete_messages` manually.
321
+ #
322
+ # @option options [Proc] :before_request (nil) Called before each
323
+ # polling attempt. This proc receives a single argument, an
324
+ # instance of {PollerStats}.
325
+ #
326
+ # @return [PollerStats]
327
+ def poll(options = {}, &block)
328
+ config = @default_config.with(options)
329
+ stats = PollerStats.new
330
+ catch(:stop_polling) do
331
+ loop do
332
+ messages = get_messages(config, stats)
333
+ if messages.empty?
334
+ check_idle_timeout(config, stats, messages)
335
+ else
336
+ process_messages(config, stats, messages, &block)
337
+ end
338
+ end
339
+ end
340
+ stats.polling_stopped_at = Time.now
341
+ stats
342
+ end
343
+
344
+ # @note This method should be called from inside a {#poll} block.
345
+ # @param [#receipt_handle] message An object that responds to
346
+ # `#receipt_handle`.
347
+ # @param [Integer] seconds
348
+ def change_message_visibility_timeout(message, seconds)
349
+ @client.change_message_visibility({
350
+ queue_url: @queue_url,
351
+ receipt_handle: message.receipt_handle,
352
+ visibility_timeout: seconds,
353
+ })
354
+ end
355
+
356
+ # @note This method should be called from inside a {#poll} block.
357
+ # @param [#receipt_handle] message An object that responds to
358
+ # `#receipt_handle`.
359
+ def delete_message(message)
360
+ @client.delete_message({
361
+ queue_url: @queue_url,
362
+ receipt_handle: message.receipt_handle,
363
+ })
364
+ end
365
+
366
+ # @note This method should be called from inside a {#poll} block.
367
+ # @param [Array<#message_id, #receipt_handle>] messages An array of received
368
+ # messages. Each object must respond to `#message_id` and
369
+ # `#receipt_handle`.
370
+ def delete_messages(messages)
371
+ @client.delete_message_batch(
372
+ queue_url: @queue_url,
373
+ entries: messages.map { |msg|
374
+ { id: msg.message_id, receipt_handle: msg.receipt_handle }
375
+ }
376
+ )
377
+ end
378
+
379
+ private
380
+
381
+ def get_messages(config, stats)
382
+ config.before_request.call(stats) if config.before_request
383
+ messages = send_request(config).messages
384
+ stats.request_count += 1
385
+ messages
386
+ end
387
+
388
+ def send_request(config)
389
+ params = config.request_params.merge(queue_url: @queue_url)
390
+ @client.receive_message(params)
391
+ end
392
+
393
+ def check_idle_timeout(config, stats, messages)
394
+ if config.idle_timeout
395
+ since = stats.last_message_received_at || stats.polling_started_at
396
+ idle_time = Time.now - since
397
+ throw :stop_polling if idle_time > config.idle_timeout
398
+ end
399
+ end
400
+
401
+ def process_messages(config, stats, messages, &block)
402
+ stats.received_message_count += messages.count
403
+ stats.last_message_received_at = Time.now
404
+ catch(:skip_delete) do
405
+ yield_messages(config, messages, stats, &block)
406
+ delete_messages(messages) unless config.skip_delete
407
+ end
408
+ end
409
+
410
+ def yield_messages(config, messages, stats, &block)
411
+ if config.request_params[:max_number_of_messages] == 1
412
+ messages.each do |msg|
413
+ yield(msg, stats)
414
+ end
415
+ else
416
+ yield(messages, stats)
417
+ end
418
+ end
419
+
420
+ # Statistics tracked client-side by the {QueuePoller}.
421
+ class PollerStats
422
+
423
+ def initialize
424
+ @request_count = 0
425
+ @received_message_count = 0
426
+ @last_message_received_at = nil
427
+ @polling_started_at = Time.now
428
+ @polling_stopped_at = nil
429
+ end
430
+
431
+ # @return [Integer]
432
+ attr_accessor :request_count
433
+
434
+ # @return [Integer]
435
+ attr_accessor :received_message_count
436
+
437
+ # @return [Time,nil]
438
+ attr_accessor :last_message_received_at
439
+
440
+ # @return [Time]
441
+ attr_accessor :polling_started_at
442
+
443
+ # @return [Time,nil]
444
+ attr_accessor :polling_stopped_at
445
+
446
+ end
447
+
448
+ # A read-only set of configuration used by the QueuePoller.
449
+ class PollerConfig
450
+
451
+ # @api private
452
+ CONFIG_OPTIONS = Set.new([
453
+ :idle_timeout,
454
+ :skip_delete,
455
+ :before_request,
456
+ ])
457
+
458
+ # @api private
459
+ PARAM_OPTIONS = Set.new([
460
+ :wait_time_seconds,
461
+ :max_number_of_messages,
462
+ :visibility_timeout,
463
+ :attribute_names,
464
+ :message_attribute_names,
465
+ ])
466
+
467
+ # @return [Integer,nil]
468
+ attr_reader :idle_timeout
469
+
470
+ # @return [Boolean]
471
+ attr_reader :skip_delete
472
+
473
+ # @return [Proc,nil]
474
+ attr_reader :before_request
475
+
476
+ # @return [Hash]
477
+ attr_reader :request_params
478
+
479
+ def initialize(options)
480
+ @idle_timeout = nil
481
+ @skip_delete = false
482
+ @before_request = nil
483
+ @request_params = {
484
+ wait_time_seconds: 20,
485
+ max_number_of_messages: 1,
486
+ visibility_timeout: nil,
487
+ attribute_names: ['All'],
488
+ message_attribute_names: ['All'],
489
+ }
490
+ options.each do |opt_name, value|
491
+ if CONFIG_OPTIONS.include?(opt_name)
492
+ instance_variable_set("@#{opt_name}", value)
493
+ elsif PARAM_OPTIONS.include?(opt_name)
494
+ @request_params[opt_name] = value
495
+ else
496
+ raise ArgumentError, "invalid option #{opt_name.inspect}"
497
+ end
498
+ end
499
+ @request_params.freeze
500
+ freeze
501
+ end
502
+
503
+ # @return [PollerConfig] Returns a new {PollerConfig} instance
504
+ # with the given options applied.
505
+ def with(options)
506
+ self.class.new(to_h.merge(options))
507
+ end
508
+
509
+ private
510
+
511
+ def to_h
512
+ hash = {}
513
+ CONFIG_OPTIONS.each { |key| hash[key] = send(key) }
514
+ PARAM_OPTIONS.each { |key| hash[key] = @request_params[key] }
515
+ hash
516
+ end
517
+
518
+ end
519
+ end
520
+ end
521
+ end