ably-rest 0.8.14 → 0.8.15
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.
- checksums.yaml +4 -4
- data/lib/submodules/ably-ruby/CHANGELOG.md +48 -61
- data/lib/submodules/ably-ruby/README.md +6 -0
- data/lib/submodules/ably-ruby/ably.gemspec +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/http_paginated_response.rb +90 -0
- data/lib/submodules/ably-ruby/lib/ably/models/paginated_result.rb +5 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +30 -15
- data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +25 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +3 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +13 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +3 -3
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +60 -3
- data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +29 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +8 -6
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +52 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +44 -8
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +79 -38
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +2 -4
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +69 -21
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +10 -12
- data/lib/submodules/ably-ruby/spec/unit/models/http_paginated_result_spec.rb +380 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +109 -51
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +3 -3
- metadata +5 -4
@@ -32,6 +32,11 @@ module Ably::Models
|
|
32
32
|
@make_async = options.fetch(:async_blocking_operations, false)
|
33
33
|
|
34
34
|
@items = http_response.body
|
35
|
+
if @items.nil? || @items.to_s.strip.empty?
|
36
|
+
@items = []
|
37
|
+
end
|
38
|
+
@items = [@items] if @items.kind_of?(Hash)
|
39
|
+
|
35
40
|
@items = coerce_items_into(items, @coerce_into) if @coerce_into
|
36
41
|
@items = items.map { |item| yield item } if block_given?
|
37
42
|
end
|
@@ -50,18 +50,14 @@ module Ably
|
|
50
50
|
#
|
51
51
|
# @return [void]
|
52
52
|
def on(*event_names, &block)
|
53
|
-
event_names
|
54
|
-
callbacks[callbacks_event_coerced(event_name)] << proc_for_block(block)
|
55
|
-
end
|
53
|
+
add_callback event_names, proc_for_block(block)
|
56
54
|
end
|
57
55
|
|
58
56
|
# Equivalent of {#on} but any exception raised in a block will bubble up and cause this client library to fail.
|
59
57
|
# This method should only be used internally by the client library.
|
60
58
|
# @api private
|
61
59
|
def unsafe_on(*event_names, &block)
|
62
|
-
event_names
|
63
|
-
callbacks[callbacks_event_coerced(event_name)] << proc_for_block(block, unsafe: true)
|
64
|
-
end
|
60
|
+
add_callback event_names, proc_for_block(block, unsafe: true)
|
65
61
|
end
|
66
62
|
|
67
63
|
# On receiving an event maching the event_name, call the provided block only once and remove the registered callback
|
@@ -70,24 +66,20 @@ module Ably
|
|
70
66
|
#
|
71
67
|
# @return [void]
|
72
68
|
def once(*event_names, &block)
|
73
|
-
event_names
|
74
|
-
callbacks[callbacks_event_coerced(event_name)] << proc_for_block(block, delete_once_run: true)
|
75
|
-
end
|
69
|
+
add_callback event_names, proc_for_block(block, delete_once_run: true)
|
76
70
|
end
|
77
71
|
|
78
72
|
# Equivalent of {#once} but any exception raised in a block will bubble up and cause this client library to fail.
|
79
73
|
# This method should only be used internally by the client library.
|
80
74
|
# @api private
|
81
75
|
def unsafe_once(*event_names, &block)
|
82
|
-
event_names
|
83
|
-
callbacks[callbacks_event_coerced(event_name)] << proc_for_block(block, delete_once_run: true, unsafe: true)
|
84
|
-
end
|
76
|
+
add_callback event_names, proc_for_block(block, delete_once_run: true, unsafe: true)
|
85
77
|
end
|
86
78
|
|
87
79
|
# Emit an event with event_name that will in turn call all matching callbacks setup with `on`
|
88
80
|
def emit(event_name, *args)
|
89
|
-
callbacks[callbacks_event_coerced(event_name)].
|
90
|
-
clone.
|
81
|
+
[callbacks_any, callbacks[callbacks_event_coerced(event_name)]].each do |callback_arr|
|
82
|
+
callback_arr.clone.
|
91
83
|
select do |proc_hash|
|
92
84
|
if proc_hash[:unsafe]
|
93
85
|
proc_hash[:emit_proc].call(*args)
|
@@ -95,8 +87,9 @@ module Ably
|
|
95
87
|
safe_yield proc_hash[:emit_proc], *args
|
96
88
|
end
|
97
89
|
end.each do |callback|
|
98
|
-
|
90
|
+
callback_arr.delete callback
|
99
91
|
end
|
92
|
+
end
|
100
93
|
end
|
101
94
|
|
102
95
|
# Remove all callbacks for event_name.
|
@@ -121,6 +114,14 @@ module Ably
|
|
121
114
|
callbacks[callbacks_event_coerced(event_name)].clear
|
122
115
|
end
|
123
116
|
end
|
117
|
+
|
118
|
+
if event_names.empty?
|
119
|
+
if block_given?
|
120
|
+
callbacks_any.delete_if { |proc_hash| proc_hash[:block] == block }
|
121
|
+
else
|
122
|
+
callbacks_any.clear
|
123
|
+
end
|
124
|
+
end
|
124
125
|
end
|
125
126
|
|
126
127
|
private
|
@@ -128,6 +129,16 @@ module Ably
|
|
128
129
|
klass.extend ClassMethods
|
129
130
|
end
|
130
131
|
|
132
|
+
def add_callback(event_names, proc_block)
|
133
|
+
if event_names.empty?
|
134
|
+
callbacks_any << proc_block
|
135
|
+
else
|
136
|
+
event_names.each do |event_name|
|
137
|
+
callbacks[callbacks_event_coerced(event_name)] << proc_block
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
131
142
|
# Create a Hash with a proc that calls the provided block and returns true if option :delete_once_run is set to true.
|
132
143
|
# #emit automatically deletes any blocks that return true thus allowing a block to be run once
|
133
144
|
def proc_for_block(block, options = {})
|
@@ -145,6 +156,10 @@ module Ably
|
|
145
156
|
@callbacks ||= Hash.new { |hash, key| hash[key] = [] }
|
146
157
|
end
|
147
158
|
|
159
|
+
def callbacks_any
|
160
|
+
@callbacks_any ||= []
|
161
|
+
end
|
162
|
+
|
148
163
|
def callbacks_event_coerced(event_name)
|
149
164
|
if self.class.event_emitter_coerce_proc
|
150
165
|
self.class.event_emitter_coerce_proc.call(event_name)
|
@@ -32,6 +32,14 @@ module Ably::Modules
|
|
32
32
|
as_json.to_json(*args)
|
33
33
|
end
|
34
34
|
|
35
|
+
# Like to_json but encodes all binary fields to hex
|
36
|
+
def to_safe_json(*args)
|
37
|
+
as_json.
|
38
|
+
each_with_object({}) do |(key, val), obj|
|
39
|
+
obj[key] = to_safe_jsonable_val(val)
|
40
|
+
end.to_json(*args)
|
41
|
+
end
|
42
|
+
|
35
43
|
# @!attribute [r] hash
|
36
44
|
# @return [Integer] Compute a hash-code for this hash. Two hashes with the same content will have the same hash code
|
37
45
|
def hash
|
@@ -39,6 +47,23 @@ module Ably::Modules
|
|
39
47
|
end
|
40
48
|
|
41
49
|
private
|
50
|
+
def to_safe_jsonable_val(val)
|
51
|
+
case val
|
52
|
+
when Array
|
53
|
+
val.map { |array_val| to_safe_jsonable_val(array_val) }
|
54
|
+
when Hash
|
55
|
+
val.each_with_object({}) { |(key, hash_val), obj| obj[key] = to_safe_jsonable_val(hash_val) }
|
56
|
+
when String
|
57
|
+
if val.encoding == Encoding::ASCII_8BIT
|
58
|
+
val.unpack("H*").first
|
59
|
+
else
|
60
|
+
val
|
61
|
+
end
|
62
|
+
else
|
63
|
+
val
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
42
67
|
def ensure_utf8_string_for(attribute, value)
|
43
68
|
if value
|
44
69
|
raise ArgumentError, "#{attribute} must be a String" unless value.kind_of?(String)
|
@@ -58,7 +58,7 @@ module Ably::Realtime
|
|
58
58
|
# all messages awaiting an ACK response should fail immediately
|
59
59
|
def fail_messages_awaiting_ack(error)
|
60
60
|
# Allow a short time for other queued operations to complete before failing all messages
|
61
|
-
EventMachine.
|
61
|
+
EventMachine.next_tick do
|
62
62
|
error = Ably::Exceptions::MessageDeliveryFailed.new("Channel cannot publish messages whilst state is '#{channel.state}'") unless error
|
63
63
|
fail_messages_in_queue connection.__pending_message_ack_queue__, error
|
64
64
|
fail_messages_in_queue connection.__outgoing_message_queue__, error
|
@@ -76,10 +76,10 @@ module Ably::Realtime
|
|
76
76
|
|
77
77
|
def nack_messages(protocol_message, error)
|
78
78
|
(protocol_message.messages + protocol_message.presence).each do |message|
|
79
|
-
logger.debug "Calling NACK failure callbacks for #{message.class.name} - #{message.
|
79
|
+
logger.debug "Calling NACK failure callbacks for #{message.class.name} - #{message.to_safe_json}, protocol message: #{protocol_message}"
|
80
80
|
message.fail error
|
81
81
|
end
|
82
|
-
logger.debug "Calling NACK failure callbacks for #{protocol_message.class.name} - #{protocol_message.
|
82
|
+
logger.debug "Calling NACK failure callbacks for #{protocol_message.class.name} - #{protocol_message.to_safe_json}"
|
83
83
|
protocol_message.fail error
|
84
84
|
end
|
85
85
|
|
@@ -68,6 +68,9 @@ module Ably
|
|
68
68
|
#
|
69
69
|
# @param (see Ably::Rest::Client#initialize)
|
70
70
|
# @option options (see Ably::Rest::Client#initialize)
|
71
|
+
# @option options [Proc] :auth_callback when provided, the Proc will be called with the token params hash as the first argument, whenever a new token is required.
|
72
|
+
# Whilst the proc is called synchronously, it does not block the EventMachine reactor as it is run in a separate thread.
|
73
|
+
# The Proc should return a token string, {Ably::Models::TokenDetails} or JSON equivalent, {Ably::Models::TokenRequest} or JSON equivalent
|
71
74
|
# @option options [Boolean] :queue_messages If false, this disables the default behaviour whereby the library queues messages on a connection in the disconnected or connecting states
|
72
75
|
# @option options [Boolean] :echo_messages If false, prevents messages originating from this connection being echoed back on the same connection
|
73
76
|
# @option options [String] :recover When a recover option is specified a connection inherits the state of a previous connection that may have existed under a different instance of the Realtime library, please refer to the API documentation for further information on connection state recovery
|
@@ -144,6 +147,16 @@ module Ably
|
|
144
147
|
connection.connect(&block)
|
145
148
|
end
|
146
149
|
|
150
|
+
# (see Ably::Rest::Client#request)
|
151
|
+
# @yield [Ably::Models::HttpPaginatedResponse<>] An Array of Stats
|
152
|
+
#
|
153
|
+
# @return [Ably::Util::SafeDeferrable]
|
154
|
+
def request(method, path, params = {}, body = nil, headers = {}, &callback)
|
155
|
+
async_wrap(callback) do
|
156
|
+
rest_client.request(method, path, params, body, headers, async_blocking_operations: true)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
147
160
|
# @!attribute [r] endpoint
|
148
161
|
# @return [URI::Generic] Default Ably Realtime endpoint used for all requests
|
149
162
|
def endpoint
|
@@ -178,14 +178,14 @@ module Ably::Realtime
|
|
178
178
|
|
179
179
|
def ack_messages(messages)
|
180
180
|
messages.each do |message|
|
181
|
-
logger.debug "Calling ACK success callbacks for #{message.class.name} - #{message.
|
181
|
+
logger.debug "Calling ACK success callbacks for #{message.class.name} - #{message.to_safe_json}"
|
182
182
|
message.succeed message
|
183
183
|
end
|
184
184
|
end
|
185
185
|
|
186
186
|
def nack_messages(messages, protocol_message)
|
187
187
|
messages.each do |message|
|
188
|
-
logger.debug "Calling NACK failure callbacks for #{message.class.name} - #{message.
|
188
|
+
logger.debug "Calling NACK failure callbacks for #{message.class.name} - #{message.to_safe_json}, protocol message: #{protocol_message}"
|
189
189
|
message.fail protocol_message.error
|
190
190
|
end
|
191
191
|
end
|
@@ -200,7 +200,7 @@ module Ably::Realtime
|
|
200
200
|
return unless ensure_presence_message_is_valid(presence_message)
|
201
201
|
|
202
202
|
unless should_update_member?(presence_message)
|
203
|
-
logger.debug "#{self.class.name}: Skipped presence member #{presence_message.action} on channel #{presence.channel.name}.\n#{presence_message.
|
203
|
+
logger.debug "#{self.class.name}: Skipped presence member #{presence_message.action} on channel #{presence.channel.name}.\n#{presence_message.to_safe_json}"
|
204
204
|
return
|
205
205
|
end
|
206
206
|
|
@@ -239,13 +239,13 @@ module Ably::Realtime
|
|
239
239
|
end
|
240
240
|
|
241
241
|
def add_presence_member(presence_message)
|
242
|
-
logger.debug "#{self.class.name}: Member '#{presence_message.member_key}' for event '#{presence_message.action}' #{members.has_key?(presence_message.member_key) ? 'updated' : 'added'}.\n#{presence_message.
|
242
|
+
logger.debug "#{self.class.name}: Member '#{presence_message.member_key}' for event '#{presence_message.action}' #{members.has_key?(presence_message.member_key) ? 'updated' : 'added'}.\n#{presence_message.to_safe_json}"
|
243
243
|
members[presence_message.member_key] = { present: true, message: presence_message }
|
244
244
|
presence.emit_message presence_message.action, presence_message
|
245
245
|
end
|
246
246
|
|
247
247
|
def remove_presence_member(presence_message)
|
248
|
-
logger.debug "#{self.class.name}: Member '#{presence_message.member_key}' removed.\n#{presence_message.
|
248
|
+
logger.debug "#{self.class.name}: Member '#{presence_message.member_key}' removed.\n#{presence_message.to_safe_json}"
|
249
249
|
|
250
250
|
if in_sync?
|
251
251
|
members.delete presence_message.member_key
|
@@ -251,15 +251,67 @@ module Ably
|
|
251
251
|
# Perform an HTTP GET request to the API using configured authentication
|
252
252
|
#
|
253
253
|
# @return [Faraday::Response]
|
254
|
+
#
|
255
|
+
# @api private
|
254
256
|
def get(path, params = {}, options = {})
|
255
|
-
|
257
|
+
raw_request(:get, path, params, options)
|
256
258
|
end
|
257
259
|
|
258
260
|
# Perform an HTTP POST request to the API using configured authentication
|
259
261
|
#
|
260
262
|
# @return [Faraday::Response]
|
263
|
+
#
|
264
|
+
# @api private
|
261
265
|
def post(path, params, options = {})
|
262
|
-
|
266
|
+
raw_request(:post, path, params, options)
|
267
|
+
end
|
268
|
+
|
269
|
+
# Perform an HTTP request to the Ably API
|
270
|
+
# This is a convenience for customers who wish to use bleeding edge REST API functionality
|
271
|
+
# that is either not documented or is not included in the API for our client libraries.
|
272
|
+
# The REST client library provides a function to issue HTTP requests to the Ably endpoints
|
273
|
+
# with all the built in functionality of the library such as authentication, paging,
|
274
|
+
# fallback hosts, MsgPack and JSON support etc.
|
275
|
+
#
|
276
|
+
# @param method [Symbol] The HTTP method symbol such as +:get+, +:post+, +:put+
|
277
|
+
# @param path [String] The path of the URL such +/channel/foo/publish+
|
278
|
+
# @param params [Hash, nil] Optional querystring params
|
279
|
+
# @param body [Hash, nil] Optional body for the POST or PUT request, must be nil or a JSON-like object
|
280
|
+
# @param headers [Hash, nil] Optional additional headers
|
281
|
+
#
|
282
|
+
# @return [Ably::Models::HttpPaginatedResponse<>]
|
283
|
+
def request(method, path, params = {}, body = nil, headers = {}, options = {})
|
284
|
+
raise "Method #{method.to_s.upcase} not supported" unless [:get, :put, :post].include?(method.to_sym)
|
285
|
+
|
286
|
+
response = case method.to_sym
|
287
|
+
when :get
|
288
|
+
reauthorise_on_authorisation_failure do
|
289
|
+
send_request(method, path, params, headers: headers)
|
290
|
+
end
|
291
|
+
when :post
|
292
|
+
path_with_params = Addressable::URI.new
|
293
|
+
path_with_params.query_values = params || {}
|
294
|
+
query = path_with_params.query
|
295
|
+
reauthorise_on_authorisation_failure do
|
296
|
+
send_request(method, "#{path}#{"?#{query}" unless query.nil? || query.empty?}", body, headers: headers)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
paginated_options = {
|
301
|
+
async_blocking_operations: options.delete(:async_blocking_operations),
|
302
|
+
}
|
303
|
+
|
304
|
+
Ably::Models::HttpPaginatedResponse.new(response, path, self, paginated_options)
|
305
|
+
|
306
|
+
rescue Exceptions::ResourceMissing, Exceptions::ForbiddenRequest, Exceptions::ResourceMissing => e
|
307
|
+
response = Models::HttpPaginatedResponse::ErrorResponse.new(e.status, e.code, e.message)
|
308
|
+
Models::HttpPaginatedResponse.new(response, path, self)
|
309
|
+
rescue Exceptions::TokenExpired, Exceptions::UnauthorizedRequest => e
|
310
|
+
response = Models::HttpPaginatedResponse::ErrorResponse.new(e.status, e.code, e.message)
|
311
|
+
Models::HttpPaginatedResponse.new(response, path, self)
|
312
|
+
rescue Exceptions::InvalidRequest, Exceptions::ServerError => e
|
313
|
+
response = Models::HttpPaginatedResponse::ErrorResponse.new(e.status, e.code, e.message)
|
314
|
+
Models::HttpPaginatedResponse.new(response, path, self)
|
263
315
|
end
|
264
316
|
|
265
317
|
# @!attribute [r] endpoint
|
@@ -360,7 +412,7 @@ module Ably
|
|
360
412
|
end
|
361
413
|
|
362
414
|
private
|
363
|
-
def
|
415
|
+
def raw_request(method, path, params = {}, options = {})
|
364
416
|
options = options.clone
|
365
417
|
if options.delete(:disable_automatic_reauthorise) == true
|
366
418
|
send_request(method, path, params, options)
|
@@ -385,6 +437,11 @@ module Ably
|
|
385
437
|
connection(use_fallback: use_fallback).send(method, path, params) do |request|
|
386
438
|
unless options[:send_auth_header] == false
|
387
439
|
request.headers[:authorization] = auth.auth_header
|
440
|
+
if options[:headers]
|
441
|
+
options[:headers].map do |key, val|
|
442
|
+
request.headers[key] = val
|
443
|
+
end
|
444
|
+
end
|
388
445
|
end
|
389
446
|
end
|
390
447
|
|
@@ -186,6 +186,35 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
186
186
|
end
|
187
187
|
end
|
188
188
|
|
189
|
+
context 'with auth_callback blocking' do
|
190
|
+
let(:rest_auth_client) { Ably::Rest::Client.new(default_options.merge(key: api_key)) }
|
191
|
+
let(:client_options) { default_options.merge(auth_callback: auth_callback) }
|
192
|
+
let(:pause) { 5 }
|
193
|
+
|
194
|
+
context 'with a slow auth callback response' do
|
195
|
+
let(:auth_callback) do
|
196
|
+
Proc.new do
|
197
|
+
sleep pause
|
198
|
+
rest_auth_client.auth.request_token
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'asynchronously authenticates' do
|
203
|
+
timers_called = 0
|
204
|
+
block = Proc.new do
|
205
|
+
timers_called += 1
|
206
|
+
EventMachine.add_timer(0.5, &block)
|
207
|
+
end
|
208
|
+
block.call
|
209
|
+
client.connect
|
210
|
+
client.connection.on(:connected) do
|
211
|
+
expect(timers_called).to be >= (pause-1) / 0.5
|
212
|
+
stop_reactor
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
189
218
|
context 'when implicitly called, with an explicit ClientOptions client_id' do
|
190
219
|
let(:client_id) { random_str }
|
191
220
|
let(:client_options) { default_options.merge(auth_callback: Proc.new { auth_token_object }, client_id: client_id, log_level: :none) }
|
@@ -92,12 +92,14 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
92
92
|
|
93
93
|
it 'reattaches' do
|
94
94
|
channel.attach do
|
95
|
-
channel.
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
95
|
+
channel.once(:failed) do
|
96
|
+
expect(channel).to be_failed
|
97
|
+
channel.attach do
|
98
|
+
expect(channel).to be_attached
|
99
|
+
stop_reactor
|
100
|
+
end
|
100
101
|
end
|
102
|
+
channel.transition_state_machine :failed, reason: RuntimeError.new
|
101
103
|
end
|
102
104
|
end
|
103
105
|
end
|
@@ -371,7 +373,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
371
373
|
expect(message_id.uniq.count).to eql(1)
|
372
374
|
|
373
375
|
# Check that messages use index 0,1,2 in the ID
|
374
|
-
message_indexes = messages.map { |msg| msg.id.split(':')
|
376
|
+
message_indexes = messages.map { |msg| msg.id.split(':').last }
|
375
377
|
expect(message_indexes).to include("0", "1", "2")
|
376
378
|
stop_reactor
|
377
379
|
end
|
@@ -229,5 +229,57 @@ describe Ably::Realtime::Client, :event_machine do
|
|
229
229
|
stop_reactor
|
230
230
|
end
|
231
231
|
end
|
232
|
+
|
233
|
+
context '#request (#RSC19*)' do
|
234
|
+
let(:client_options) { default_options.merge(key: api_key) }
|
235
|
+
|
236
|
+
context 'get' do
|
237
|
+
it 'returns an HttpPaginatedResponse object' do
|
238
|
+
subject.request(:get, 'time').callback do |response|
|
239
|
+
expect(response).to be_a(Ably::Models::HttpPaginatedResponse)
|
240
|
+
expect(response.status_code).to eql(200)
|
241
|
+
stop_reactor
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
context '404 request to invalid URL' do
|
246
|
+
it 'returns an object with 404 status code and error message' do
|
247
|
+
subject.request(:get, 'does-not-exist').callback do |response|
|
248
|
+
expect(response).to be_a(Ably::Models::HttpPaginatedResponse)
|
249
|
+
expect(response.error_message).to match(/Could not find/)
|
250
|
+
expect(response.error_code).to eql(40400)
|
251
|
+
expect(response.status_code).to eql(404)
|
252
|
+
stop_reactor
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context 'paged results' do
|
258
|
+
let(:channel_name) { random_str }
|
259
|
+
|
260
|
+
it 'provides paging' do
|
261
|
+
10.times do
|
262
|
+
subject.rest_client.request(:post, "/channels/#{channel_name}/publish", {}, { 'name': 'test' })
|
263
|
+
end
|
264
|
+
|
265
|
+
subject.request(:get, "/channels/#{channel_name}/messages", { limit: 2 }).callback do |response|
|
266
|
+
expect(response.items.length).to eql(2)
|
267
|
+
expect(response).to be_has_next
|
268
|
+
response.next do |next_page|
|
269
|
+
expect(next_page.items.length).to eql(2)
|
270
|
+
expect(next_page).to be_has_next
|
271
|
+
first_page_ids = response.items.map { |message| message['id'] }.uniq.sort
|
272
|
+
next_page_ids = next_page.items.map { |message| message['id'] }.uniq.sort
|
273
|
+
expect(first_page_ids).to_not eql(next_page_ids)
|
274
|
+
next_page.next do |third_page|
|
275
|
+
expect(third_page.items.length).to eql(2)
|
276
|
+
stop_reactor
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
232
284
|
end
|
233
285
|
end
|