ably-rest 1.0.0 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -4
- data/lib/submodules/ably-ruby/.travis.yml +3 -0
- data/lib/submodules/ably-ruby/CHANGELOG.md +49 -4
- data/lib/submodules/ably-ruby/LICENSE +2 -2
- data/lib/submodules/ably-ruby/README.md +25 -6
- data/lib/submodules/ably-ruby/ably.gemspec +1 -1
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +4 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +18 -4
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +22 -3
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +44 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +2 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +5 -2
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +27 -2
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +95 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +6 -3
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +26 -7
- data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +7 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0d3b8547bb94eb6d8efd1847fee0cc7231916da
|
4
|
+
data.tar.gz: 34e92947e49eb6ff09f3bc608277f09ef1e9b390
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 155934f7ef691a2248776199b86ab243aa8e872193b28c40afaa77f08dc939d24bb1cb7592cb0bda624f4418d4a2ce7e68f969ed57c7494d4c663a25fc90e9ec
|
7
|
+
data.tar.gz: d935e38fa7b0bbbbdafc071b63bfecedcfe5693160438f802a8a18a3db106c5aeada7845485b0d128037a09e3b69b84bf6f45ed3b7180a21bf0bd582e497d705
|
data/README.md
CHANGED
@@ -5,11 +5,15 @@
|
|
5
5
|
|
6
6
|
A Ruby REST client library for [www.ably.io](https://www.ably.io), the realtime messaging service.
|
7
7
|
|
8
|
-
Note: This library was created solely for developers who do not want EventMachine as a dependency of their application. If this is not a requirement for you, then we recommended you use the combined [REST & Realtime gem](https://rubygems.org/gems/ably).
|
9
|
-
|
10
8
|
## Documentation
|
11
9
|
|
12
|
-
Visit https://www.ably.io/documentation for a complete API reference and more examples. The examples and API below is not exhaustive.
|
10
|
+
Visit https://www.ably.io/documentation for a complete API reference and more examples. The examples and API below is not exhaustive, you should use the completely [Ably API documentation](https://www.ably.io/documentation).
|
11
|
+
|
12
|
+
## Realtime vs REST
|
13
|
+
|
14
|
+
This REST only library was created for developers who do not want EventMachine as a dependency of their application. Typically developers who are using Ably within their Rails or Sinatra apps would prefer to use the REST library as it has less dependencies and offers a synchronous API.
|
15
|
+
|
16
|
+
If however you need to use a realtime library that offers an asynchronous evented AP, then we recommended you [take a look at the combined REST & Realtime gem](https://rubygems.org/gems/ably).
|
13
17
|
|
14
18
|
## Installation
|
15
19
|
|
@@ -142,6 +146,10 @@ To see what has changed in recent versions of Bundler, see the [CHANGELOG](CHANG
|
|
142
146
|
4. Push to the branch (`git push origin my-new-feature`)
|
143
147
|
5. Create a new Pull Request
|
144
148
|
|
149
|
+
## Release Process
|
150
|
+
|
151
|
+
See the [Ably Ruby release process notes](https://github.com/ably/ably-ruby#release-process).
|
152
|
+
|
145
153
|
## License
|
146
154
|
|
147
|
-
Copyright (c)
|
155
|
+
Copyright (c) 2017 Ably Real-time Ltd, Licensed under the Apache License, Version 2.0. Refer to [LICENSE](LICENSE) for the license terms.
|
@@ -1,11 +1,56 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## [
|
4
|
-
|
3
|
+
## [Unreleased](https://github.com/ably/ably-ruby/tree/v1.0.5)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/ably/ably-ruby/compare/v1.0.4...v1.0.5)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Add Ruby 2.1 and 2.3 to Travis tests [\#129](https://github.com/ably/ably-ruby/issues/129)
|
10
|
+
- Add supported platforms to README file [\#128](https://github.com/ably/ably-ruby/issues/128)
|
11
|
+
- Add Ruby 2.1 and 2.3 to Travis tests [\#130](https://github.com/ably/ably-ruby/pull/130) ([funkyboy](https://github.com/funkyboy))
|
12
|
+
|
13
|
+
**Closed issues:**
|
14
|
+
|
15
|
+
- Cannot get realtime to work [\#127](https://github.com/ably/ably-ruby/issues/127)
|
16
|
+
|
17
|
+
**Merged pull requests:**
|
18
|
+
|
19
|
+
- Improve pagination history test [\#138](https://github.com/ably/ably-ruby/pull/138) ([funkyboy](https://github.com/funkyboy))
|
20
|
+
- Fix failing auth test [\#135](https://github.com/ably/ably-ruby/pull/135) ([funkyboy](https://github.com/funkyboy))
|
21
|
+
- Add submodule instructions to Contributing section [\#134](https://github.com/ably/ably-ruby/pull/134) ([funkyboy](https://github.com/funkyboy))
|
22
|
+
- Add request\_id option to client [\#133](https://github.com/ably/ably-ruby/pull/133) ([funkyboy](https://github.com/funkyboy))
|
23
|
+
- Update README with supported platforms [\#131](https://github.com/ably/ably-ruby/pull/131) ([funkyboy](https://github.com/funkyboy))
|
24
|
+
|
25
|
+
## [v1.0.4](https://github.com/ably/ably-ruby/tree/v1.0.4) (2017-05-31)
|
26
|
+
[Full Changelog](https://github.com/ably/ably-ruby/compare/v1.0.3...v1.0.4)
|
5
27
|
|
6
|
-
|
28
|
+
## [v1.0.3](https://github.com/ably/ably-ruby/tree/v1.0.3) (2017-05-31)
|
29
|
+
[Full Changelog](https://github.com/ably/ably-ruby/compare/v1.0.2...v1.0.3)
|
30
|
+
|
31
|
+
## [v1.0.2](https://github.com/ably/ably-ruby/tree/v1.0.2) (2017-05-16)
|
32
|
+
[Full Changelog](https://github.com/ably/ably-ruby/compare/v1.0.1...v1.0.2)
|
33
|
+
|
34
|
+
**Fixed bugs:**
|
35
|
+
|
36
|
+
- Reconnect following disconnection is hitting a 403 error [\#117](https://github.com/ably/ably-ruby/issues/117)
|
37
|
+
|
38
|
+
**Merged pull requests:**
|
39
|
+
|
40
|
+
- Fallback fixes [\#120](https://github.com/ably/ably-ruby/pull/120) ([mattheworiordan](https://github.com/mattheworiordan))
|
41
|
+
- Channel name encoding error for REST requests [\#119](https://github.com/ably/ably-ruby/pull/119) ([mattheworiordan](https://github.com/mattheworiordan))
|
42
|
+
|
43
|
+
## [v1.0.1](https://github.com/ably/ably-ruby/tree/v1.0.1) (2017-05-11)
|
44
|
+
[Full Changelog](https://github.com/ably/ably-ruby/compare/v1.1.0-beta.push.1...v1.0.1)
|
45
|
+
|
46
|
+
## [v1.1.0-beta.push.1](https://github.com/ably/ably-ruby/tree/v1.1.0-beta.push.1) (2017-04-25)
|
47
|
+
[Full Changelog](https://github.com/ably/ably-ruby/compare/v1.0.0...v1.1.0-beta.push.1)
|
48
|
+
|
49
|
+
## [v1.0.0](https://github.com/ably/ably-ruby/tree/v1.0.0) (2017-03-07)
|
50
|
+
[Full Changelog](https://github.com/ably/ably-ruby/compare/v0.8.15...v1.0.0)
|
7
51
|
|
8
|
-
|
52
|
+
## [v0.8.15](https://github.com/ably/ably-ruby/tree/v0.8.15) (2017-03-07)
|
53
|
+
[Full Changelog](https://github.com/ably/ably-ruby/compare/v0.8.14...v0.8.15)
|
9
54
|
|
10
55
|
**Implemented enhancements:**
|
11
56
|
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2015-2017 Ably
|
2
2
|
|
3
|
-
Copyright
|
3
|
+
Copyright 2015-2017 Ably Real-time Ltd
|
4
4
|
|
5
5
|
Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
you may not use this file except in compliance with the License.
|
@@ -5,6 +5,14 @@
|
|
5
5
|
|
6
6
|
A Ruby client library for [ably.io](https://www.ably.io), the realtime messaging service.
|
7
7
|
|
8
|
+
## Supported platforms
|
9
|
+
|
10
|
+
This SDK supports Ruby 1.9.3+.
|
11
|
+
|
12
|
+
We regression-test the SDK against a selection of Ruby versions (which we update over time, but usually consists of mainstream and widely used versions). Please refer to [.travis.yml](./.travis.yml) for the set of versions that currently undergo CI testing.
|
13
|
+
|
14
|
+
If you find any compatibility issues, please [do raise an issue](https://github.com/ably/ably-ruby/issues/new) in this repository or [contact Ably customer support](https://support.ably.io/) for advice.
|
15
|
+
|
8
16
|
## Documentation
|
9
17
|
|
10
18
|
Visit https://www.ably.io/documentation for a complete API reference and more examples.
|
@@ -296,12 +304,23 @@ To see what has changed in recent versions of Bundler, see the [CHANGELOG](CHANG
|
|
296
304
|
## Contributing
|
297
305
|
|
298
306
|
1. Fork it
|
299
|
-
2.
|
300
|
-
3.
|
301
|
-
4.
|
302
|
-
|
303
|
-
|
307
|
+
2. When pulling to local, make sure to also pull the `ably-common` repo (`git submodule init && git submodule update`)
|
308
|
+
3. Create your feature branch (`git checkout -b my-new-feature`)
|
309
|
+
4. Commit your changes (`git commit -am 'Add some feature'`)
|
310
|
+
5. Ensure you have added suitable tests and the test suite is passing(`bundle exec rspec`)
|
311
|
+
6. Push to the branch (`git push origin my-new-feature`)
|
312
|
+
7. Create a new Pull Request
|
313
|
+
|
314
|
+
## Release process
|
315
|
+
|
316
|
+
This library uses [semantic versioning](http://semver.org/). For each release, the following needs to be done:
|
317
|
+
|
318
|
+
* Update the version number in [version.rb](./lib/ably/version.rb) and commit the change.
|
319
|
+
* Run [`github_changelog_generator`](https://github.com/skywinder/Github-Changelog-Generator) to automate the update of the [CHANGELOG](./CHANGELOG.md). Once the `CHANGELOG` update has completed, manually change the `Unreleased` heading and link with the current version number such as `v1.0.0`. Also ensure that the `Full Changelog` link points to the new version tag instead of the `HEAD`. Commit this change.
|
320
|
+
* Add a tag and push to origin such as `git tag v1.0.0 && git push origin v1.0.0`
|
321
|
+
* Visit [https://github.com/ably/ably-ruby/tags](https://github.com/ably/ably-ruby/tags) and `Add release notes` for the release including links to the changelog entry.
|
322
|
+
* Run `rake release` to publish the gem to [Rubygems](http://www.rubydoc.info/gems/ably)
|
304
323
|
|
305
324
|
## License
|
306
325
|
|
307
|
-
Copyright (c)
|
326
|
+
Copyright (c) 2017 Ably Real-time Ltd, Licensed under the Apache License, Version 2.0. Refer to [LICENSE](LICENSE) for the license terms.
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.description = %q{A Ruby client library for ably.io realtime messaging}
|
12
12
|
spec.summary = %q{A Ruby client library for ably.io realtime messaging implemented using EventMachine}
|
13
13
|
spec.homepage = 'http://github.com/ably/ably-ruby'
|
14
|
-
spec.license = 'Apache
|
14
|
+
spec.license = 'Apache-2.0'
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
@@ -13,7 +13,7 @@ module Ably
|
|
13
13
|
# @!attribute [r] code
|
14
14
|
# @return [String] Ably specific error code
|
15
15
|
class BaseAblyException < StandardError
|
16
|
-
attr_reader :status, :code
|
16
|
+
attr_reader :status, :code, :request_id
|
17
17
|
|
18
18
|
def initialize(message, status = nil, code = nil, base_exception = nil, options = {})
|
19
19
|
super message
|
@@ -25,6 +25,7 @@ module Ably
|
|
25
25
|
@code = code
|
26
26
|
@code ||= base_exception.code if base_exception && base_exception.respond_to?(:code)
|
27
27
|
@code ||= options[:fallback_code]
|
28
|
+
@request_id ||= options[:request_id]
|
28
29
|
end
|
29
30
|
|
30
31
|
def to_s
|
@@ -34,12 +35,13 @@ module Ably
|
|
34
35
|
additional_info << "code: #{code}" if code
|
35
36
|
additional_info << "http status: #{status}" if status
|
36
37
|
additional_info << "base exception: #{@base_exception.class}" if @base_exception
|
38
|
+
additional_info << "request_id: #{request_id}" if request_id
|
37
39
|
message << "(#{additional_info.join(', ')})"
|
38
40
|
end
|
39
41
|
message.join(' ')
|
40
42
|
end
|
41
43
|
|
42
|
-
def as_json
|
44
|
+
def as_json(*args)
|
43
45
|
{
|
44
46
|
message: "#{self.class}: #{message}",
|
45
47
|
status: @status,
|
@@ -351,7 +351,7 @@ module Ably
|
|
351
351
|
def determine_host
|
352
352
|
raise ArgumentError, 'Block required' unless block_given?
|
353
353
|
|
354
|
-
if
|
354
|
+
if should_use_fallback_hosts?
|
355
355
|
internet_up? do |internet_is_up_result|
|
356
356
|
@current_host = if internet_is_up_result
|
357
357
|
client.fallback_endpoint.host
|
@@ -424,7 +424,8 @@ module Ably
|
|
424
424
|
)
|
425
425
|
|
426
426
|
# Use native websocket heartbeats if possible
|
427
|
-
|
427
|
+
# TODO: Fix once https://github.com/ably/ably-ruby/issues/116 is resolved
|
428
|
+
url_params['heartbeats'] = 'true' # unless defaults.fetch(:websocket_heartbeats_disabled)
|
428
429
|
|
429
430
|
url_params['clientId'] = client.auth.client_id if client.auth.has_client_id?
|
430
431
|
|
@@ -444,6 +445,10 @@ module Ably
|
|
444
445
|
end
|
445
446
|
|
446
447
|
determine_host do |host|
|
448
|
+
# Ensure the hostname matches the fallback host name
|
449
|
+
url.hostname = host
|
450
|
+
url.port = port
|
451
|
+
|
447
452
|
begin
|
448
453
|
logger.debug { "Connection: Opening socket connection to #{host}:#{port}/#{url.path}?#{url.query}" }
|
449
454
|
@transport = create_transport(host, port, url) do |websocket_transport|
|
@@ -509,6 +514,7 @@ module Ably
|
|
509
514
|
|
510
515
|
# @api private
|
511
516
|
def create_transport(host, port, url, &block)
|
517
|
+
logger.debug { "Connection: EventMachine connecting to #{host}:#{port} with URL: #{url}" }
|
512
518
|
EventMachine.connect(host, port, WebsocketTransport, self, url.to_s, &block)
|
513
519
|
end
|
514
520
|
|
@@ -641,14 +647,22 @@ module Ably
|
|
641
647
|
!!client.custom_realtime_host
|
642
648
|
end
|
643
649
|
|
644
|
-
def
|
650
|
+
def should_use_fallback_hosts?
|
645
651
|
if client.fallback_hosts && !client.fallback_hosts.empty?
|
646
|
-
if connecting? && previous_state
|
652
|
+
if connecting? && previous_state && !disconnected_from_connected_state?
|
647
653
|
use_fallback_if_disconnected? || use_fallback_if_suspended?
|
648
654
|
end
|
649
655
|
end
|
650
656
|
end
|
651
657
|
|
658
|
+
def disconnected_from_connected_state?
|
659
|
+
most_recent_state_changes = state_history.last(3).first(2) # Ignore current state
|
660
|
+
|
661
|
+
# A valid connection was disconnected
|
662
|
+
most_recent_state_changes.last.fetch(:state) == Connection::STATE.Disconnected &&
|
663
|
+
most_recent_state_changes.first.fetch(:state) == Connection::STATE.Connected
|
664
|
+
end
|
665
|
+
|
652
666
|
def use_fallback_if_disconnected?
|
653
667
|
second_reconnect_attempt_for(:disconnected, 1)
|
654
668
|
end
|
@@ -83,6 +83,10 @@ module Ably
|
|
83
83
|
# if empty or nil then fallback host functionality is disabled
|
84
84
|
attr_reader :fallback_hosts
|
85
85
|
|
86
|
+
# Whethere the {Client} has to add a random identifier to the path of a request
|
87
|
+
# @return [Boolean]
|
88
|
+
attr_reader :add_request_ids
|
89
|
+
|
86
90
|
# Creates a {Ably::Rest::Client Rest Client} and configures the {Ably::Auth} object for the connection.
|
87
91
|
#
|
88
92
|
# @param [Hash,String] options an options Hash used to configure the client and the authentication, or String with an API key or Token ID
|
@@ -146,6 +150,7 @@ module Ably
|
|
146
150
|
@custom_host = options.delete(:rest_host)
|
147
151
|
@custom_port = options.delete(:port)
|
148
152
|
@custom_tls_port = options.delete(:tls_port)
|
153
|
+
@add_request_ids = options.delete(:add_request_ids)
|
149
154
|
|
150
155
|
if options[:fallback_hosts_use_default] && options[:fallback_jhosts]
|
151
156
|
raise ArgumentError, "fallback_hosts_use_default cannot be set to trye when fallback_jhosts is also provided"
|
@@ -434,11 +439,25 @@ module Ably
|
|
434
439
|
max_retry_duration = http_defaults.fetch(:max_retry_duration)
|
435
440
|
requested_at = Time.now
|
436
441
|
retry_count = 0
|
442
|
+
request_id = nil
|
443
|
+
if add_request_ids
|
444
|
+
params = if params.nil?
|
445
|
+
{}
|
446
|
+
else
|
447
|
+
params.dup
|
448
|
+
end
|
449
|
+
request_id = SecureRandom.urlsafe_base64(10)
|
450
|
+
params[:request_id] = request_id
|
451
|
+
end
|
437
452
|
|
438
453
|
begin
|
439
454
|
use_fallback = can_fallback_to_alternate_ably_host? && retry_count > 0
|
440
455
|
|
441
456
|
connection(use_fallback: use_fallback).send(method, path, params) do |request|
|
457
|
+
if add_request_ids
|
458
|
+
request.options.context = {} if request.options.context.nil?
|
459
|
+
request.options.context[:request_id] = request_id
|
460
|
+
end
|
442
461
|
unless options[:send_auth_header] == false
|
443
462
|
request.headers[:authorization] = auth.auth_header
|
444
463
|
if options[:headers]
|
@@ -456,12 +475,12 @@ module Ably
|
|
456
475
|
logger.warn { "Ably::Rest::Client - Retry #{retry_count} for #{method} #{path} #{params} as initial attempt failed: #{error}" }
|
457
476
|
retry
|
458
477
|
end
|
459
|
-
|
460
478
|
case error
|
461
479
|
when Faraday::TimeoutError
|
462
|
-
raise Ably::Exceptions::ConnectionTimeout.new(error.message, nil, 80014, error)
|
480
|
+
raise Ably::Exceptions::ConnectionTimeout.new(error.message, nil, 80014, error, { request_id: request_id })
|
463
481
|
when Faraday::ClientError
|
464
|
-
|
482
|
+
# request_id is also available in the request context
|
483
|
+
raise Ably::Exceptions::ConnectionError.new(error.message, nil, 80000, error, { request_id: request_id })
|
465
484
|
else
|
466
485
|
raise error
|
467
486
|
end
|
@@ -27,8 +27,8 @@ module Ably
|
|
27
27
|
end
|
28
28
|
|
29
29
|
message = 'Unknown server error' if message.to_s.strip == ''
|
30
|
-
|
31
|
-
exception_args = [message, error_status_code, error_code]
|
30
|
+
request_id = env.request.context[:request_id] if env.request.context
|
31
|
+
exception_args = [message, error_status_code, error_code, nil, { request_id: request_id }]
|
32
32
|
|
33
33
|
if env.status >= 500
|
34
34
|
raise Ably::Exceptions::ServerError.new(*exception_args)
|
@@ -1178,6 +1178,27 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
1178
1178
|
stop_reactor
|
1179
1179
|
end
|
1180
1180
|
end
|
1181
|
+
|
1182
|
+
it 'does not use a fallback host if the connection connects on the default host and then later becomes disconnected', em_timeout: 25 do
|
1183
|
+
request = 0
|
1184
|
+
|
1185
|
+
allow(connection).to receive(:create_transport).and_wrap_original do |wrapped_proc, host, *args, &block|
|
1186
|
+
expect(host).to eql(expected_host)
|
1187
|
+
request += 1
|
1188
|
+
wrapped_proc.call(host, *args, &block)
|
1189
|
+
end
|
1190
|
+
|
1191
|
+
connection.on(:connected) do
|
1192
|
+
if request <= 2
|
1193
|
+
EventMachine.add_timer(3) do
|
1194
|
+
# Force a disconnect
|
1195
|
+
connection.transport.unbind
|
1196
|
+
end
|
1197
|
+
else
|
1198
|
+
stop_reactor
|
1199
|
+
end
|
1200
|
+
end
|
1201
|
+
end
|
1181
1202
|
end
|
1182
1203
|
|
1183
1204
|
context ':fallback_hosts array is provided' do
|
@@ -1286,6 +1307,29 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
1286
1307
|
end
|
1287
1308
|
end
|
1288
1309
|
end
|
1310
|
+
|
1311
|
+
it 'uses the correct host name for the WebSocket requests to the fallback hosts' do
|
1312
|
+
request = 0
|
1313
|
+
expect(connection).to receive(:create_transport).at_least(:once) do |host, port, uri|
|
1314
|
+
if request == 0 || request == expected_retry_attempts + 1
|
1315
|
+
expect(uri.hostname).to eql(expected_host)
|
1316
|
+
else
|
1317
|
+
expect(custom_hosts + [expected_host]).to include(uri.hostname)
|
1318
|
+
fallback_hosts_used << host if @suspended > 0
|
1319
|
+
end
|
1320
|
+
request += 1
|
1321
|
+
raise EventMachine::ConnectionError
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
connection.on(:suspended) do
|
1325
|
+
@suspended += 1
|
1326
|
+
|
1327
|
+
if @suspended > 4
|
1328
|
+
expect(fallback_hosts_used.uniq).to match_array(custom_hosts + [expected_host])
|
1329
|
+
stop_reactor
|
1330
|
+
end
|
1331
|
+
end
|
1332
|
+
end
|
1289
1333
|
end
|
1290
1334
|
|
1291
1335
|
context ':fallback_hosts array is provided by an empty array' do
|
@@ -980,6 +980,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
980
980
|
|
981
981
|
context 'transport-level heartbeats are supported in the websocket transport' do
|
982
982
|
it 'provides the heartbeats argument in the websocket connection params (#RTN23b)' do
|
983
|
+
skip 'Native heartbeats not yet supported in the WS driver https://github.com/ably/ably-ruby/issues/116'
|
983
984
|
expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
|
984
985
|
uri = URI.parse(url)
|
985
986
|
expect(CGI::parse(uri.query)['heartbeats'][0]).to eql('false')
|
@@ -1007,6 +1008,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1007
1008
|
let(:client_options) { default_options.merge(websocket_heartbeats_disabled: true) }
|
1008
1009
|
|
1009
1010
|
it 'does not provide the heartbeats argument in the websocket connection params (#RTN23b)' do
|
1011
|
+
skip 'Native heartbeats not yet supported in the WS driver https://github.com/ably/ably-ruby/issues/116'
|
1010
1012
|
expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
|
1011
1013
|
uri = URI.parse(url)
|
1012
1014
|
expect(CGI::parse(uri.query)['heartbeats'][0]).to be_nil
|
@@ -76,6 +76,8 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
|
|
76
76
|
end
|
77
77
|
|
78
78
|
context 'with supported extra payload content type (#RTL6h, #RSL6a2)' do
|
79
|
+
let(:channel) { client.channel("pushenabled:#{random_str}") }
|
80
|
+
|
79
81
|
def publish_and_check_extras(extras)
|
80
82
|
channel.attach
|
81
83
|
channel.publish 'event', {}, extras: extras
|
@@ -86,7 +88,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
|
|
86
88
|
end
|
87
89
|
|
88
90
|
context 'JSON Object (Hash)' do
|
89
|
-
let(:data) { { 'push' => { 'title' => 'Testing' } } }
|
91
|
+
let(:data) { { 'push' => { 'notification' => { 'title' => 'Testing' } } } }
|
90
92
|
|
91
93
|
it 'is encoded and decoded to the same hash' do
|
92
94
|
publish_and_check_extras data
|
@@ -94,7 +96,8 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
|
|
94
96
|
end
|
95
97
|
|
96
98
|
context 'JSON Array' do
|
97
|
-
|
99
|
+
# TODO: Add nil type back in
|
100
|
+
let(:data) { { 'push' => { 'data' => { 'key' => [ true, false, 55, 'string', { 'Hash' => true }, ['array'] ] } } } }
|
98
101
|
|
99
102
|
it 'is encoded and decoded to the same Array' do
|
100
103
|
publish_and_check_extras data
|
@@ -1083,7 +1083,7 @@ describe Ably::Auth do
|
|
1083
1083
|
expect { auth.request_token(timestamp: Time.now - 180) }.to raise_error do |error|
|
1084
1084
|
expect(error).to be_a(Ably::Exceptions::UnauthorizedRequest)
|
1085
1085
|
expect(error.status).to eql(401)
|
1086
|
-
expect(error.code).to eql(
|
1086
|
+
expect(error.code).to eql(40104)
|
1087
1087
|
end
|
1088
1088
|
end
|
1089
1089
|
|
@@ -256,6 +256,25 @@ describe Ably::Rest::Channel do
|
|
256
256
|
end
|
257
257
|
end
|
258
258
|
end
|
259
|
+
|
260
|
+
context 'with a non ASCII channel name' do
|
261
|
+
let(:channel_name) { 'foo:¡€≤`☃' }
|
262
|
+
let(:channel_name_encoded) { 'foo%3A%C2%A1%E2%82%AC%E2%89%A4%60%E2%98%83' }
|
263
|
+
let(:endpoint) { client.endpoint }
|
264
|
+
let(:channel) { client.channels.get(channel_name) }
|
265
|
+
|
266
|
+
context 'stubbed', :webmock do
|
267
|
+
let!(:get_stub) {
|
268
|
+
stub_request(:post, "#{endpoint}/channels/#{channel_name_encoded}/publish").
|
269
|
+
to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
|
270
|
+
}
|
271
|
+
|
272
|
+
it 'correctly encodes the channel name' do
|
273
|
+
channel.publish('foo')
|
274
|
+
expect(get_stub).to have_been_requested
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
259
278
|
end
|
260
279
|
|
261
280
|
describe '#history' do
|
@@ -324,7 +343,13 @@ describe Ably::Rest::Channel do
|
|
324
343
|
|
325
344
|
# Page 3
|
326
345
|
expect(page_3.items.size).to eql(1)
|
327
|
-
|
346
|
+
# This test should be deterministic but it's not.
|
347
|
+
# Sometimes the backend, to avoid too much work, returns a `next` link that contains empty reults.
|
348
|
+
if page_3.next
|
349
|
+
expect(page_3.next.items.length).to eql(0)
|
350
|
+
else
|
351
|
+
expect(page_3).to be_last
|
352
|
+
end
|
328
353
|
end
|
329
354
|
|
330
355
|
context 'direction' do
|
@@ -374,7 +399,7 @@ describe Ably::Rest::Channel do
|
|
374
399
|
let!(:history_stub) {
|
375
400
|
query_params = default_history_options
|
376
401
|
.merge(option => milliseconds).map { |k, v| "#{k}=#{v}" }.join('&')
|
377
|
-
stub_request(:get, "#{endpoint}/channels/#{
|
402
|
+
stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/messages?#{query_params}").
|
378
403
|
to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
|
379
404
|
}
|
380
405
|
|
@@ -959,5 +959,100 @@ describe Ably::Rest::Client do
|
|
959
959
|
end
|
960
960
|
end
|
961
961
|
end
|
962
|
+
|
963
|
+
context 'request_id generation' do
|
964
|
+
context 'Timeout error' do
|
965
|
+
context 'with request_id', :webmock do
|
966
|
+
let(:custom_logger) do
|
967
|
+
Class.new do
|
968
|
+
def initialize
|
969
|
+
@messages = []
|
970
|
+
end
|
971
|
+
|
972
|
+
[:fatal, :error, :warn, :info, :debug].each do |severity|
|
973
|
+
define_method severity do |message, &block|
|
974
|
+
message_val = [message]
|
975
|
+
message_val << block.call if block
|
976
|
+
|
977
|
+
@messages << [severity, message_val.compact.join(' ')]
|
978
|
+
end
|
979
|
+
end
|
980
|
+
|
981
|
+
def logs
|
982
|
+
@messages
|
983
|
+
end
|
984
|
+
|
985
|
+
def level
|
986
|
+
1
|
987
|
+
end
|
988
|
+
|
989
|
+
def level=(new_level)
|
990
|
+
end
|
991
|
+
end
|
992
|
+
end
|
993
|
+
let(:custom_logger_object) { custom_logger.new }
|
994
|
+
let(:client_options) { default_options.merge(key: api_key, logger: custom_logger_object, add_request_ids: true) }
|
995
|
+
before do
|
996
|
+
@request_id = nil
|
997
|
+
stub_request(:get, Addressable::Template.new("#{client.endpoint}/time{?request_id}")).with do |request|
|
998
|
+
@request_id = request.uri.query_values['request_id']
|
999
|
+
end.to_return do
|
1000
|
+
raise Faraday::TimeoutError.new('timeout error message')
|
1001
|
+
end
|
1002
|
+
end
|
1003
|
+
it 'has an error with the same request_id of the request' do
|
1004
|
+
expect{ client.time }.to raise_error(Ably::Exceptions::ConnectionTimeout, /#{@request_id}/)
|
1005
|
+
expect(custom_logger_object.logs.find { |severity, message| message.match(/#{@request_id}/i)} ).to_not be_nil
|
1006
|
+
end
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
context 'when specifying fallback hosts', :webmock do
|
1010
|
+
let(:client_options) { { key: api_key, fallback_hosts_use_default: true, add_request_ids: true } }
|
1011
|
+
let(:requests) { [] }
|
1012
|
+
before do
|
1013
|
+
@request_id = nil
|
1014
|
+
hosts = Ably::FALLBACK_HOSTS + ['rest.ably.io']
|
1015
|
+
hosts.each do |host|
|
1016
|
+
stub_request(:get, Addressable::Template.new("https://#{host.downcase}/time{?request_id}")).with do |request|
|
1017
|
+
@request_id = request.uri.query_values['request_id']
|
1018
|
+
requests << @request_id
|
1019
|
+
end.to_return do
|
1020
|
+
raise Faraday::TimeoutError.new('timeout error message')
|
1021
|
+
end
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
it 'request_id is the same across retries' do
|
1025
|
+
expect{ client.time }.to raise_error(Ably::Exceptions::ConnectionTimeout, /#{@request_id}/)
|
1026
|
+
expect(requests.uniq.count).to eql(1)
|
1027
|
+
expect(requests.uniq.first).to eql(@request_id)
|
1028
|
+
end
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
context 'without request_id' do
|
1032
|
+
let(:client_options) { default_options.merge(key: api_key, http_request_timeout: 0) }
|
1033
|
+
it 'does not include request_id in ConnectionTimeout error' do
|
1034
|
+
begin
|
1035
|
+
client.stats
|
1036
|
+
rescue Ably::Exceptions::ConnectionTimeout => err
|
1037
|
+
expect(err.request_id).to eql(nil)
|
1038
|
+
end
|
1039
|
+
end
|
1040
|
+
end
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
context 'UnauthorizedRequest nonce error' do
|
1044
|
+
let(:token_params) { { nonce: "samenonce_#{protocol}", timestamp: Time.now.to_i } }
|
1045
|
+
it 'includes request_id in UnauthorizedRequest error due to replayed nonce' do
|
1046
|
+
client1 = Ably::Rest::Client.new(default_options.merge(key: api_key))
|
1047
|
+
client2 = Ably::Rest::Client.new(default_options.merge(key: api_key, add_request_ids: true))
|
1048
|
+
expect { client1.auth.request_token(token_params) }.not_to raise_error
|
1049
|
+
begin
|
1050
|
+
client2.auth.request_token(token_params)
|
1051
|
+
rescue Ably::Exceptions::UnauthorizedRequest => err
|
1052
|
+
expect(err.request_id).to_not eql(nil)
|
1053
|
+
end
|
1054
|
+
end
|
1055
|
+
end
|
1056
|
+
end
|
962
1057
|
end
|
963
1058
|
end
|
@@ -62,8 +62,10 @@ describe Ably::Rest::Channel, 'messages' do
|
|
62
62
|
end
|
63
63
|
|
64
64
|
context 'with supported extra payload content type (#RSL1h, #RSL6a2)' do
|
65
|
+
let(:channel) { client.channel("pushenabled:#{random_str}") }
|
66
|
+
|
65
67
|
context 'JSON Object (Hash)' do
|
66
|
-
let(:data) { { 'push' => { 'title' => 'Testing' } } }
|
68
|
+
let(:data) { { 'push' => { 'notification' => { 'title' => 'Testing' } } } }
|
67
69
|
|
68
70
|
it 'is encoded and decoded to the same hash' do
|
69
71
|
channel.publish 'event', {}, extras: data
|
@@ -72,9 +74,10 @@ describe Ably::Rest::Channel, 'messages' do
|
|
72
74
|
end
|
73
75
|
|
74
76
|
context 'JSON Array' do
|
75
|
-
|
77
|
+
# TODO: Add nil type back in
|
78
|
+
let(:data) { { 'push' => { 'data' => { 'key' => [ true, false, 55, 'string', { 'Hash' => true }, ['array'] ] } } } }
|
76
79
|
|
77
|
-
it 'is encoded and decoded to the same
|
80
|
+
it 'is encoded and decoded to the same deep multi-type object' do
|
78
81
|
channel.publish 'event', {}, extras: data
|
79
82
|
expect(channel.history.items.first.extras).to eql(data)
|
80
83
|
end
|
@@ -73,7 +73,7 @@ describe Ably::Rest::Presence do
|
|
73
73
|
end
|
74
74
|
let!(:get_stub) {
|
75
75
|
query_params = query_options.map { |k, v| "#{k}=#{v}" }.join('&')
|
76
|
-
stub_request(:get, "#{endpoint}/channels/#{
|
76
|
+
stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence?#{query_params}").
|
77
77
|
to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
|
78
78
|
}
|
79
79
|
let(:channel_name) { random_str }
|
@@ -111,6 +111,25 @@ describe Ably::Rest::Presence do
|
|
111
111
|
expect(fixtures_channel.presence.get(connection_id: 'does.not.exist').items).to be_empty
|
112
112
|
end
|
113
113
|
end
|
114
|
+
|
115
|
+
context 'with a non ASCII channel name' do
|
116
|
+
let(:channel_name) { 'foo:¡€≤`☃' }
|
117
|
+
let(:channel_name_encoded) { 'foo%3A%C2%A1%E2%82%AC%E2%89%A4%60%E2%98%83' }
|
118
|
+
let(:endpoint) { client.endpoint }
|
119
|
+
let(:channel) { client.channels.get(channel_name) }
|
120
|
+
|
121
|
+
context 'stubbed', :webmock do
|
122
|
+
let!(:get_stub) {
|
123
|
+
stub_request(:get, "#{endpoint}/channels/#{channel_name_encoded}/presence?limit=100").
|
124
|
+
to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
|
125
|
+
}
|
126
|
+
|
127
|
+
it 'correctly encodes the channel name' do
|
128
|
+
channel.presence.get
|
129
|
+
expect(get_stub).to have_been_requested
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
114
133
|
end
|
115
134
|
|
116
135
|
describe '#history' do
|
@@ -194,7 +213,7 @@ describe Ably::Rest::Presence do
|
|
194
213
|
context 'limit options', :webmock do
|
195
214
|
let!(:history_stub) {
|
196
215
|
query_params = history_options.map { |k, v| "#{k}=#{v}" }.join('&')
|
197
|
-
stub_request(:get, "#{endpoint}/channels/#{
|
216
|
+
stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?#{query_params}").
|
198
217
|
to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
|
199
218
|
}
|
200
219
|
|
@@ -234,7 +253,7 @@ describe Ably::Rest::Presence do
|
|
234
253
|
}
|
235
254
|
let!(:history_stub) {
|
236
255
|
query_params = history_options.map { |k, v| "#{k}=#{v}" }.join('&')
|
237
|
-
stub_request(:get, "#{endpoint}/channels/#{
|
256
|
+
stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?#{query_params}").
|
238
257
|
to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
|
239
258
|
}
|
240
259
|
|
@@ -338,7 +357,7 @@ describe Ably::Rest::Presence do
|
|
338
357
|
|
339
358
|
context '#get' do
|
340
359
|
let!(:get_stub) {
|
341
|
-
stub_request(:get, "#{endpoint}/channels/#{
|
360
|
+
stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence?limit=100").
|
342
361
|
to_return(:body => serialized_encoded_message, :headers => { 'Content-Type' => content_type })
|
343
362
|
}
|
344
363
|
|
@@ -355,7 +374,7 @@ describe Ably::Rest::Presence do
|
|
355
374
|
|
356
375
|
context '#history' do
|
357
376
|
let!(:history_stub) {
|
358
|
-
stub_request(:get, "#{endpoint}/channels/#{
|
377
|
+
stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?direction=backwards&limit=100").
|
359
378
|
to_return(:body => serialized_encoded_message, :headers => { 'Content-Type' => content_type })
|
360
379
|
}
|
361
380
|
|
@@ -385,7 +404,7 @@ describe Ably::Rest::Presence do
|
|
385
404
|
context '#get' do
|
386
405
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
387
406
|
let!(:get_stub) {
|
388
|
-
stub_request(:get, "#{endpoint}/channels/#{
|
407
|
+
stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence?limit=100").
|
389
408
|
to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type })
|
390
409
|
}
|
391
410
|
let(:presence_message) { presence.get.items.first }
|
@@ -409,7 +428,7 @@ describe Ably::Rest::Presence do
|
|
409
428
|
context '#history' do
|
410
429
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
411
430
|
let!(:history_stub) {
|
412
|
-
stub_request(:get, "#{endpoint}/channels/#{
|
431
|
+
stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?direction=backwards&limit=100").
|
413
432
|
to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type })
|
414
433
|
}
|
415
434
|
let(:presence_message) { presence.history.items.first }
|
@@ -50,4 +50,11 @@ describe Ably::Rest::Client do
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
53
|
+
|
54
|
+
context 'request_id generation' do
|
55
|
+
let(:client_options) { { key: 'appid.keyuid:keysecret', add_request_ids: true } }
|
56
|
+
it 'includes request_id in URL' do
|
57
|
+
expect(subject.add_request_ids).to eql(true)
|
58
|
+
end
|
59
|
+
end
|
53
60
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ably-rest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew O'Riordan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|