aws-sdk-sqs 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ # utility classes
2
+ require 'aws-sdk-sqs/queue_poller'
@@ -0,0 +1,23 @@
1
+ # WARNING ABOUT GENERATED CODE
2
+ #
3
+ # This file is generated. See the contributing for info on making contributions:
4
+ # https://github.com/aws/aws-sdk-ruby/blob/master/CONTRIBUTING.md
5
+ #
6
+ # WARNING ABOUT GENERATED CODE
7
+
8
+ module Aws
9
+ module SQS
10
+ module Errors
11
+
12
+ extend Aws::Errors::DynamicErrors
13
+
14
+ # Raised when calling #load or #data on a resource class that can not be
15
+ # loaded. This can happen when:
16
+ #
17
+ # * A resource class has identifiers, but no data attributes.
18
+ # * Resource data is only available when making an API call that
19
+ # enumerates all resources of that type.
20
+ class ResourceNotLoadable < RuntimeError; end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,166 @@
1
+ require 'openssl'
2
+
3
+ module Aws
4
+ module SQS
5
+ module Plugins
6
+ class Md5s < Seahorse::Client::Plugin
7
+
8
+ # @api private
9
+ class Handler < Seahorse::Client::Handler
10
+ def call(context)
11
+ @handler.call(context).on_success do |response|
12
+ case context.operation_name
13
+ when :send_message
14
+ validate_send_message(context, response)
15
+ when :send_message_batch
16
+ validate_send_message_batch(context, response)
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ TRANSPORT_TYPE_ENCODINGS = {
24
+ 'String' => 1,
25
+ 'Binary' => 2,
26
+ 'Number' => 1
27
+ }
28
+
29
+ DATA_TYPE = /\A(String|Binary|Number)(\..+)?\z/
30
+
31
+ NORMALIZED_ENCODING = Encoding::UTF_8
32
+
33
+ def validate_send_message(context, response)
34
+ body = context.params[:message_body]
35
+ attributes = context.params[:message_attributes]
36
+ validate_single_message(body, attributes, response)
37
+ end
38
+
39
+ def validate_send_message_batch(context, response)
40
+ context.params[:entries].each do |entry|
41
+ id = entry[:id]
42
+ body = entry[:message_body]
43
+ attributes = entry[:message_attributes]
44
+ message_response = response.successful.select { |r| r.id == id }[0]
45
+ unless message_response.nil?
46
+ validate_single_message(body, attributes, message_response)
47
+ end
48
+ end
49
+ end
50
+
51
+ def validate_single_message(body, attributes, response)
52
+ validate_body(body, response)
53
+ validate_attributes(attributes, response) unless attributes.nil?
54
+ end
55
+
56
+ def validate_body(body, response)
57
+ calculated_md5 = md5_of_message_body(body)
58
+ returned_md5 = response.md5_of_message_body
59
+ if calculated_md5 != returned_md5
60
+ error_message = mismatch_error_message(
61
+ 'message body',
62
+ calculated_md5,
63
+ returned_md5,
64
+ response)
65
+ raise Aws::Errors::ChecksumError, error_message
66
+ end
67
+ end
68
+
69
+ def validate_attributes(attributes, response)
70
+ calculated_md5 = md5_of_message_attributes(attributes)
71
+ returned_md5 = response.md5_of_message_attributes
72
+ if returned_md5 != calculated_md5
73
+ error_message = mismatch_error_message(
74
+ 'message atributes',
75
+ calculated_md5,
76
+ returned_md5,
77
+ response)
78
+ raise Aws::Errors::ChecksumError, error_message
79
+ end
80
+ end
81
+
82
+ def md5_of_message_body(message_body)
83
+ OpenSSL::Digest::MD5.hexdigest(message_body)
84
+ end
85
+
86
+ def md5_of_message_attributes(message_attributes)
87
+ encoded = { }
88
+ message_attributes.each do |name, attribute|
89
+ name = name.to_s
90
+ encoded[name] = String.new
91
+ data_type_without_label = DATA_TYPE.match(attribute[:data_type])[1]
92
+ encoded[name] << encode_length_and_bytes(name) <<
93
+ encode_length_and_bytes(attribute[:data_type]) <<
94
+ [TRANSPORT_TYPE_ENCODINGS[data_type_without_label]].pack('C'.freeze)
95
+
96
+ if attribute[:string_value] != nil
97
+ encoded[name] << encode_length_and_string(attribute[:string_value])
98
+ elsif attribute[:binary_value] != nil
99
+ encoded[name] << encode_length_and_bytes(attribute[:binary_value])
100
+ end
101
+ end
102
+
103
+ buffer = encoded.keys.sort.reduce(String.new) do |string, name|
104
+ string << encoded[name]
105
+ end
106
+ OpenSSL::Digest::MD5.hexdigest(buffer)
107
+ end
108
+
109
+ def encode_length_and_string(string)
110
+ string = String.new(string)
111
+ string.encode!(NORMALIZED_ENCODING)
112
+ encode_length_and_bytes(string)
113
+ end
114
+
115
+ def encode_length_and_bytes(bytes)
116
+ [bytes.bytesize, bytes].pack('L>a*'.freeze)
117
+ end
118
+
119
+ def mismatch_error_message(section, local_md5, returned_md5, response)
120
+ m = "MD5 returned by SQS does not match " <<
121
+ "the calculation on the original request. ("
122
+
123
+ if response.respond_to?(:id) && !response.id.nil?
124
+ m << "Message ID: #{response.id}, "
125
+ end
126
+
127
+ m << "MD5 calculated by the #{section}: " <<
128
+ "'#{local_md5}', MD5 checksum returned: '#{returned_md5}')"
129
+ end
130
+ end
131
+
132
+ option(:verify_checksums,
133
+ doc_default: true,
134
+ doc_type: 'Boolean',
135
+ docstring: <<-DOCS
136
+ When `true` MD5 checksums will be computed for messages sent to
137
+ an SQS queue and matched against MD5 checksums returned by Amazon SQS.
138
+ `Aws::Errors::Checksum` errors are raised for cases where checksums do
139
+ not match.
140
+ DOCS
141
+ ) do |config|
142
+ # By default, we will disable checksum verification when response
143
+ # stubbing is enable. If a user decides to enable both features,
144
+ # then they will need to stub the MD5s in the response.
145
+ # See the spec/aws/sqs/client/verify_checksums_spec.rb for
146
+ # examples of how to do this.
147
+ if config.respond_to?(:stub_responses)
148
+ !config.stub_responses
149
+ else
150
+ config.verify_checksums
151
+ end
152
+ end
153
+
154
+ def add_handlers(handlers, config)
155
+ if config.verify_checksums
156
+ handlers.add(Handler, {
157
+ priority: 10 ,
158
+ step: :validate,
159
+ operations: [:send_message, :send_message_batch]
160
+ })
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,43 @@
1
+ module Aws
2
+ module SQS
3
+ module Plugins
4
+ # @api private
5
+ class QueueUrls < Seahorse::Client::Plugin
6
+
7
+ class Handler < Seahorse::Client::Handler
8
+
9
+ def call(context)
10
+ if queue_url = context.params[:queue_url]
11
+ update_endpoint(context, queue_url)
12
+ update_region(context, queue_url)
13
+ end
14
+ @handler.call(context)
15
+ end
16
+
17
+ def update_endpoint(context, url)
18
+ context.http_request.endpoint = url
19
+ end
20
+
21
+ # If the region in the queue url is not the configured
22
+ # region, then we will modify the request to have
23
+ # a sigv4 signer for the proper region.
24
+ def update_region(context, queue_url)
25
+ if queue_region = queue_url.to_s.split('.')[1]
26
+ if queue_region != context.config.region
27
+ config = context.config.dup
28
+ config.region = queue_region
29
+ config.sigv4_region = queue_region
30
+ config.sigv4_signer = Aws::Plugins::SignatureV4.build_signer(config)
31
+ context.config = config
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ handler(Handler)
39
+
40
+ end
41
+ end
42
+ end
43
+ 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: Proc.new)
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