ably-rest 1.0.5 → 1.1.3
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 +5 -5
- data/.travis.yml +6 -3
- data/CHANGELOG.md +1 -1
- data/LICENSE +1 -1
- data/README.md +26 -7
- data/SPEC.md +2003 -1605
- data/ably-rest.gemspec +4 -2
- data/lib/submodules/ably-ruby/.editorconfig +14 -0
- data/lib/submodules/ably-ruby/.travis.yml +10 -8
- data/lib/submodules/ably-ruby/CHANGELOG.md +97 -1
- data/lib/submodules/ably-ruby/LICENSE +1 -3
- data/lib/submodules/ably-ruby/README.md +12 -7
- data/lib/submodules/ably-ruby/Rakefile +32 -0
- data/lib/submodules/ably-ruby/SPEC.md +1277 -835
- data/lib/submodules/ably-ruby/ably.gemspec +17 -11
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +34 -8
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +10 -4
- data/lib/submodules/ably-ruby/lib/ably/logger.rb +8 -2
- data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/device_details.rb +87 -0
- data/lib/submodules/ably-ruby/lib/ably/models/device_push_details.rb +86 -0
- data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +23 -2
- data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +12 -12
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +6 -4
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +6 -4
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +32 -2
- data/lib/submodules/ably-ruby/lib/ably/models/push_channel_subscription.rb +89 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/exception_codes.rb +128 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +15 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/safe_deferrable.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/modules/safe_yield.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +5 -5
- data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +1 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +27 -105
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +4 -8
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/publisher.rb +74 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/push_channel.rb +62 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +91 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +9 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +45 -26
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +25 -9
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +7 -7
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +9 -9
- data/lib/submodules/ably-ruby/lib/ably/realtime/push.rb +40 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/push/admin.rb +61 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/push/device_registrations.rb +105 -0
- data/lib/submodules/ably-ruby/lib/ably/rest.rb +1 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +54 -18
- data/lib/submodules/ably-ruby/lib/ably/rest/channel/push_channel.rb +62 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +171 -41
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +17 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push.rb +42 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push/admin.rb +54 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push/channel_subscriptions.rb +121 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push/device_registrations.rb +103 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +7 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +253 -49
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +33 -21
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +180 -62
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +155 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +293 -13
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +142 -39
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +38 -36
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +12 -3
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +207 -173
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/push_admin_spec.rb +736 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/push_spec.rb +27 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +62 -51
- data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +79 -4
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +6 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +318 -74
- data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +158 -6
- data/lib/submodules/ably-ruby/spec/acceptance/rest/push_admin_spec.rb +952 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/push_spec.rb +25 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/run_parallel_tests +33 -0
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +1 -9
- data/lib/submodules/ably-ruby/spec/spec_helper.rb +3 -1
- data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +9 -5
- data/lib/submodules/ably-ruby/spec/support/event_emitter_helper.rb +31 -0
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +2 -2
- data/lib/submodules/ably-ruby/spec/support/test_logger_helper.rb +42 -0
- data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +11 -12
- data/lib/submodules/ably-ruby/spec/unit/models/device_details_spec.rb +102 -0
- data/lib/submodules/ably-ruby/spec/unit/models/device_push_details_spec.rb +101 -0
- data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +51 -3
- data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +17 -2
- data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/models/push_channel_subscription_spec.rb +86 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +3 -3
- data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +10 -10
- data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +13 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/push_channel_spec.rb +36 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +30 -1
- data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +30 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/push_channel_spec.rb +36 -0
- data/lib/submodules/ably-ruby/spec/unit/util/pub_sub_spec.rb +3 -3
- data/spec/spec_helper.rb +1 -0
- metadata +51 -10
@@ -10,6 +10,14 @@ module Ably
|
|
10
10
|
env.body = parse(env.body) unless env.response_headers['Ably-Middleware-Parsed'] == true
|
11
11
|
env.response_headers['Ably-Middleware-Parsed'] = true
|
12
12
|
end
|
13
|
+
rescue Ably::Exceptions::InvalidResponseBody => e
|
14
|
+
debug_info = {
|
15
|
+
method: env.method,
|
16
|
+
url: env.url,
|
17
|
+
base64_body: base64_body(env.body),
|
18
|
+
response_headers: env.response_headers
|
19
|
+
}
|
20
|
+
raise Ably::Exceptions::InvalidResponseBody, "#{e.message}\nRequest env: #{debug_info}"
|
13
21
|
end
|
14
22
|
|
15
23
|
def parse(body)
|
@@ -18,8 +26,16 @@ module Ably
|
|
18
26
|
else
|
19
27
|
body
|
20
28
|
end
|
29
|
+
rescue MessagePack::UnknownExtTypeError => e
|
30
|
+
raise Ably::Exceptions::InvalidResponseBody, "MessagePack::UnknownExtTypeError body could not be decoded: #{e.message}. Got Base64:\n#{base64_body(body)}"
|
21
31
|
rescue MessagePack::MalformedFormatError => e
|
22
|
-
raise Ably::Exceptions::InvalidResponseBody, "
|
32
|
+
raise Ably::Exceptions::InvalidResponseBody, "MessagePack::MalformedFormatError body could not be decoded: #{e.message}. Got Base64:\n#{base64_body(body)}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def base64_body(body)
|
36
|
+
Base64.encode64(body)
|
37
|
+
rescue => err
|
38
|
+
"[#{err.message}! Could not base64 encode body: '#{body}']"
|
23
39
|
end
|
24
40
|
end
|
25
41
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'ably/rest/push/admin'
|
2
|
+
|
3
|
+
module Ably
|
4
|
+
module Rest
|
5
|
+
# Class providing push notification functionality
|
6
|
+
class Push
|
7
|
+
include Ably::Modules::Conversions
|
8
|
+
|
9
|
+
# @private
|
10
|
+
attr_reader :client
|
11
|
+
|
12
|
+
def initialize(client)
|
13
|
+
@client = client
|
14
|
+
end
|
15
|
+
|
16
|
+
# Admin features for push notifications like managing devices and channel subscriptions
|
17
|
+
# @return [Ably::Rest::Push::Admin]
|
18
|
+
def admin
|
19
|
+
@admin ||= Admin.new(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Activate this device for push notifications by registering with the push transport such as GCM/APNS
|
23
|
+
#
|
24
|
+
# @note This is unsupported in the Ruby library
|
25
|
+
def activate(*arg)
|
26
|
+
raise_unsupported
|
27
|
+
end
|
28
|
+
|
29
|
+
# Deactivate this device for push notifications by removing the registration with the push transport such as GCM/APNS
|
30
|
+
#
|
31
|
+
# @note This is unsupported in the Ruby library
|
32
|
+
def deactivate(*arg)
|
33
|
+
raise_unsupported
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def raise_unsupported
|
38
|
+
raise Ably::Exceptions::PushNotificationsNotSupported, 'This device does not support receiving or subscribing to push notifications. All PushChannel methods are unavailable'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'ably/rest/push/device_registrations'
|
2
|
+
require 'ably/rest/push/channel_subscriptions'
|
3
|
+
|
4
|
+
module Ably::Rest
|
5
|
+
class Push
|
6
|
+
# Class providing push notification administrative functionality
|
7
|
+
# for registering devices and attaching to channels etc.
|
8
|
+
class Admin
|
9
|
+
include Ably::Modules::Conversions
|
10
|
+
|
11
|
+
# @api private
|
12
|
+
attr_reader :client
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
attr_reader :push
|
16
|
+
|
17
|
+
def initialize(push)
|
18
|
+
@push = push
|
19
|
+
@client = push.client
|
20
|
+
end
|
21
|
+
|
22
|
+
# Publish a push message directly to a single recipient
|
23
|
+
#
|
24
|
+
# @param recipient [Hash] A recipient device, client_id or raw APNS/GCM target. Refer to push documentation
|
25
|
+
# @param data [Hash] The notification payload data and fields. Refer to push documentation
|
26
|
+
#
|
27
|
+
# @return [void]
|
28
|
+
#
|
29
|
+
def publish(recipient, data)
|
30
|
+
raise ArgumentError, "Expecting a Hash object for recipient, got #{recipient.class}" unless recipient.kind_of?(Hash)
|
31
|
+
raise ArgumentError, "Recipient data is empty. You must provide recipient details" if recipient.empty?
|
32
|
+
raise ArgumentError, "Expecting a Hash object for data, got #{data.class}" unless data.kind_of?(Hash)
|
33
|
+
raise ArgumentError, "Push data field is empty. You must provide attributes for the push notification" if data.empty?
|
34
|
+
|
35
|
+
publish_data = data.merge(recipient: IdiomaticRubyWrapper(recipient))
|
36
|
+
# Co-erce to camelCase for notitication fields which are always camelCase
|
37
|
+
publish_data[:notification] = IdiomaticRubyWrapper(data[:notification]) if publish_data[:notification].kind_of?(Hash)
|
38
|
+
client.post('/push/publish', publish_data)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Manage device registrations
|
42
|
+
# @return [Ably::Rest::Push::DeviceRegistrations]
|
43
|
+
def device_registrations
|
44
|
+
@device_registrations ||= DeviceRegistrations.new(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Manage channel subscriptions for devices or clients
|
48
|
+
# @return [Ably::Rest::Push::ChannelSubscriptions]
|
49
|
+
def channel_subscriptions
|
50
|
+
@channel_subscriptions ||= ChannelSubscriptions.new(self)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Ably::Rest
|
2
|
+
class Push
|
3
|
+
# Manage push notification channel subscriptions for devices or client identifiers
|
4
|
+
class ChannelSubscriptions
|
5
|
+
include Ably::Modules::Conversions
|
6
|
+
|
7
|
+
# @api private
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
# @api private
|
11
|
+
attr_reader :admin
|
12
|
+
|
13
|
+
def initialize(admin)
|
14
|
+
@admin = admin
|
15
|
+
@client = admin.client
|
16
|
+
end
|
17
|
+
|
18
|
+
# List channel subscriptions filtered by optional params
|
19
|
+
#
|
20
|
+
# @param [Hash] params the filter options for the list channel subscription request. At least one of channel, client_id or device_id is required
|
21
|
+
# @option params [String] :channel filter by realtime pub/sub channel name
|
22
|
+
# @option params [String] :client_id filter by devices registered to a client identifier. If provided with device_id param, a concat operation is used so that any device with this client_id or provided device_id is returned.
|
23
|
+
# @option params [String] :device_id filter by unique device ID. If provided with client_id param, a concat operation is used so that any device with this device_id or provided client_id is returned.
|
24
|
+
# @option params [Integer] :limit maximum number of subscriptions to retrieve up to 1,000, defaults to 100
|
25
|
+
#
|
26
|
+
# @return [Ably::Models::PaginatedResult<Ably::Models::PushChannelSubscription>] Paginated list of matching {Ably::Models::PushChannelSubscription}
|
27
|
+
#
|
28
|
+
def list(params)
|
29
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
30
|
+
|
31
|
+
if (IdiomaticRubyWrapper(params).keys & [:channel, :client_id, :device_id]).length == 0
|
32
|
+
raise ArgumentError, "at least one channel, client_id or device_id filter param must be provided"
|
33
|
+
end
|
34
|
+
|
35
|
+
params = params.clone
|
36
|
+
|
37
|
+
paginated_options = {
|
38
|
+
coerce_into: 'Ably::Models::PushChannelSubscription',
|
39
|
+
async_blocking_operations: params.delete(:async_blocking_operations),
|
40
|
+
}
|
41
|
+
|
42
|
+
response = client.get('/push/channelSubscriptions', IdiomaticRubyWrapper(params).as_json)
|
43
|
+
|
44
|
+
Ably::Models::PaginatedResult.new(response, '', client, paginated_options)
|
45
|
+
end
|
46
|
+
|
47
|
+
# List channels with at least one subscribed device
|
48
|
+
#
|
49
|
+
# @param [Hash] params the options for the list channels request
|
50
|
+
# @option params [Integer] :limit maximum number of channels to retrieve up to 1,000, defaults to 100
|
51
|
+
#
|
52
|
+
# @return [Ably::Models::PaginatedResult<String>] Paginated list of matching {Ably::Models::PushChannelSubscription}
|
53
|
+
#
|
54
|
+
def list_channels(params = {})
|
55
|
+
params = {} if params.nil?
|
56
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
57
|
+
|
58
|
+
params = params.clone
|
59
|
+
|
60
|
+
paginated_options = {
|
61
|
+
coerce_into: 'String',
|
62
|
+
async_blocking_operations: params.delete(:async_blocking_operations),
|
63
|
+
}
|
64
|
+
|
65
|
+
response = client.get('/push/channels', IdiomaticRubyWrapper(params).as_json)
|
66
|
+
|
67
|
+
Ably::Models::PaginatedResult.new(response, '', client, paginated_options)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Save push channel subscription for a device or client ID
|
71
|
+
#
|
72
|
+
# @param [Ably::Models::PushChannelSubscription,Hash] push_channel_subscription the push channel subscription details to save
|
73
|
+
#
|
74
|
+
# @return [void]
|
75
|
+
#
|
76
|
+
def save(push_channel_subscription)
|
77
|
+
push_channel_subscription_object = PushChannelSubscription(push_channel_subscription)
|
78
|
+
raise ArgumentError, "Channel is required yet is empty" if push_channel_subscription_object.channel.to_s.empty?
|
79
|
+
|
80
|
+
client.post("/push/channelSubscriptions", push_channel_subscription_object.as_json)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Remove a push channel subscription
|
84
|
+
#
|
85
|
+
# @param [Ably::Models::PushChannelSubscription,Hash] push_channel_subscription the push channel subscription details to remove
|
86
|
+
#
|
87
|
+
# @return [void]
|
88
|
+
#
|
89
|
+
def remove(push_channel_subscription)
|
90
|
+
push_channel_subscription_object = PushChannelSubscription(push_channel_subscription)
|
91
|
+
raise ArgumentError, "Channel is required yet is empty" if push_channel_subscription_object.channel.to_s.empty?
|
92
|
+
if push_channel_subscription_object.client_id.to_s.empty? && push_channel_subscription_object.device_id.to_s.empty?
|
93
|
+
raise ArgumentError, "Either client_id or device_id must be present"
|
94
|
+
end
|
95
|
+
|
96
|
+
client.delete("/push/channelSubscriptions", push_channel_subscription_object.as_json)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Remove all matching push channel subscriptions
|
100
|
+
#
|
101
|
+
# @param [Hash] params the filter options for the list channel subscription request. At least one of channel, client_id or device_id is required
|
102
|
+
# @option params [String] :channel filter by realtime pub/sub channel name
|
103
|
+
# @option params [String] :client_id filter by devices registered to a client identifier. If provided with device_id param, a concat operation is used so that any device with this client_id or provided device_id is returned.
|
104
|
+
# @option params [String] :device_id filter by unique device ID. If provided with client_id param, a concat operation is used so that any device with this device_id or provided client_id is returned.
|
105
|
+
#
|
106
|
+
# @return [void]
|
107
|
+
#
|
108
|
+
def remove_where(params)
|
109
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
110
|
+
|
111
|
+
if (IdiomaticRubyWrapper(params).keys & [:channel, :client_id, :device_id]).length == 0
|
112
|
+
raise ArgumentError, "at least one channel, client_id or device_id filter param must be provided"
|
113
|
+
end
|
114
|
+
|
115
|
+
params = params.clone
|
116
|
+
|
117
|
+
client.delete("/push/channelSubscriptions", IdiomaticRubyWrapper(params).as_json)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Ably::Rest
|
2
|
+
class Push
|
3
|
+
# Manage device registrations for push notifications
|
4
|
+
class DeviceRegistrations
|
5
|
+
include Ably::Modules::Conversions
|
6
|
+
|
7
|
+
# @api private
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
# @api private
|
11
|
+
attr_reader :admin
|
12
|
+
|
13
|
+
def initialize(admin)
|
14
|
+
@admin = admin
|
15
|
+
@client = admin.client
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get registered device by device ID
|
19
|
+
#
|
20
|
+
# @param [String, Ably::Models::DeviceDetails] device_id the device to retrieve
|
21
|
+
#
|
22
|
+
# @return [Ably::Models::DeviceDetails] Returns {Ably::Models::DeviceDetails} if a match is found else a {Ably::Exceptions::ResourceMissing} is raised
|
23
|
+
#
|
24
|
+
def get(device_id)
|
25
|
+
device_id = device_id.id if device_id.kind_of?(Ably::Models::DeviceDetails)
|
26
|
+
raise ArgumentError, "device_id must be a string or DeviceDetails object" unless device_id.kind_of?(String)
|
27
|
+
|
28
|
+
DeviceDetails(client.get("/push/deviceRegistrations/#{device_id}").body)
|
29
|
+
end
|
30
|
+
|
31
|
+
# List registered devices filtered by optional params
|
32
|
+
#
|
33
|
+
# @param [Hash] params the filter options for the list registered device request
|
34
|
+
# @option params [String] :client_id filter by devices registered to a client identifier. Cannot be used with +device_id+ param
|
35
|
+
# @option params [String] :device_id filter by unique device ID. Cannot be used with +client_id+ param
|
36
|
+
# @option params [Integer] :limit maximum number of devices to retrieve up to 1,000, defaults to 100
|
37
|
+
#
|
38
|
+
# @return [Ably::Models::PaginatedResult<Ably::Models::DeviceDetails>] Paginated list of matching {Ably::Models::DeviceDetails}
|
39
|
+
#
|
40
|
+
def list(params = {})
|
41
|
+
params = {} if params.nil?
|
42
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
43
|
+
raise ArgumentError, "device_id filter cannot be specified alongside a client_id filter. Use one or the other" if params[:client_id] && params[:device_id]
|
44
|
+
|
45
|
+
params = params.clone
|
46
|
+
|
47
|
+
paginated_options = {
|
48
|
+
coerce_into: 'Ably::Models::DeviceDetails',
|
49
|
+
async_blocking_operations: params.delete(:async_blocking_operations),
|
50
|
+
}
|
51
|
+
|
52
|
+
response = client.get('/push/deviceRegistrations', IdiomaticRubyWrapper(params).as_json)
|
53
|
+
|
54
|
+
Ably::Models::PaginatedResult.new(response, '', client, paginated_options)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Save and register device
|
58
|
+
#
|
59
|
+
# @param [Ably::Models::DeviceDetails, Hash] device the device details to save
|
60
|
+
#
|
61
|
+
# @return [void]
|
62
|
+
#
|
63
|
+
def save(device)
|
64
|
+
device_details = DeviceDetails(device)
|
65
|
+
raise ArgumentError, "Device ID is required yet is empty" if device_details.id.nil? || device_details == ''
|
66
|
+
|
67
|
+
client.put("/push/deviceRegistrations/#{device_details.id}", device_details.as_json)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Remove device
|
71
|
+
#
|
72
|
+
# @param [String, Ably::Models::DeviceDetails] device_id the device to remove
|
73
|
+
#
|
74
|
+
# @return [void]
|
75
|
+
#
|
76
|
+
def remove(device_id)
|
77
|
+
device_id = device_id.id if device_id.kind_of?(Ably::Models::DeviceDetails)
|
78
|
+
raise ArgumentError, "device_id must be a string or DeviceDetails object" unless device_id.kind_of?(String)
|
79
|
+
|
80
|
+
client.delete("/push/deviceRegistrations/#{device_id}", {})
|
81
|
+
end
|
82
|
+
|
83
|
+
# Remove device matching where params
|
84
|
+
#
|
85
|
+
# @param [Hash] params the filter params for the remove request
|
86
|
+
# @option params [String] :client_id remove devices registered to a client identifier. Cannot be used with +device_id+ param
|
87
|
+
# @option params [String] :device_id remove device with this unique device ID. Cannot be used with +client_id+ param
|
88
|
+
#
|
89
|
+
# @return [void]
|
90
|
+
#
|
91
|
+
def remove_where(params = {})
|
92
|
+
filter = if params.kind_of?(Ably::Models::DeviceDetails)
|
93
|
+
{ 'deviceId' => params.id }
|
94
|
+
else
|
95
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
96
|
+
raise ArgumentError, "device_id filter cannot be specified alongside a client_id filter. Use one or the other" if params[:client_id] && params[:device_id]
|
97
|
+
IdiomaticRubyWrapper(params).as_json
|
98
|
+
end
|
99
|
+
client.delete("/push/deviceRegistrations", filter)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Ably
|
2
|
-
VERSION = '1.
|
3
|
-
PROTOCOL_VERSION = '1.
|
2
|
+
VERSION = '1.1.3'
|
3
|
+
PROTOCOL_VERSION = '1.1'
|
4
4
|
|
5
5
|
# Allow a variant to be configured for all instances of this client library
|
6
6
|
# such as ruby-rest-[VERSION]
|
@@ -13,4 +13,9 @@ module Ably
|
|
13
13
|
def self.lib_variant
|
14
14
|
@lib_variant
|
15
15
|
end
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
def self.major_minor_version_numeric
|
19
|
+
VERSION.gsub(/\.\d+$/, '').to_f
|
20
|
+
end
|
16
21
|
end
|
@@ -206,7 +206,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
206
206
|
|
207
207
|
context 'with a slow auth callback response' do
|
208
208
|
let(:auth_callback) do
|
209
|
-
|
209
|
+
lambda do |token_params|
|
210
210
|
sleep pause
|
211
211
|
rest_auth_client.auth.request_token
|
212
212
|
end
|
@@ -214,7 +214,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
214
214
|
|
215
215
|
it 'asynchronously authenticates' do
|
216
216
|
timers_called = 0
|
217
|
-
block =
|
217
|
+
block = lambda do
|
218
218
|
timers_called += 1
|
219
219
|
EventMachine.add_timer(0.5, &block)
|
220
220
|
end
|
@@ -230,7 +230,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
230
230
|
|
231
231
|
context 'when implicitly called, with an explicit ClientOptions client_id' do
|
232
232
|
let(:client_id) { random_str }
|
233
|
-
let(:client_options) { default_options.merge(auth_callback:
|
233
|
+
let(:client_options) { default_options.merge(auth_callback: lambda { |token_params| auth_token_object }, client_id: client_id, log_level: :none) }
|
234
234
|
let(:rest_auth_client) { Ably::Rest::Client.new(default_options.merge(key: api_key, client_id: 'invalid')) }
|
235
235
|
|
236
236
|
context 'and an incompatible client_id in a TokenDetails object passed to the auth callback' do
|
@@ -268,7 +268,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
268
268
|
|
269
269
|
context 'when explicitly called, with an explicit ClientOptions client_id' do
|
270
270
|
let(:auth_proc) do
|
271
|
-
|
271
|
+
lambda do |token_params|
|
272
272
|
if !@requested
|
273
273
|
@requested = true
|
274
274
|
valid_auth_token
|
@@ -304,18 +304,18 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
304
304
|
let(:rest_client) { Ably::Rest::Client.new(default_options) }
|
305
305
|
let(:client_publisher) { auto_close Ably::Realtime::Client.new(default_options) }
|
306
306
|
let(:basic_capability) { JSON.dump("foo" => ["subscribe"]) }
|
307
|
-
let(:basic_token_cb) {
|
307
|
+
let(:basic_token_cb) { lambda do |token_params|
|
308
308
|
rest_client.auth.create_token_request({ capability: basic_capability })
|
309
309
|
end }
|
310
310
|
let(:upgraded_capability) { JSON.dump({ "foo" => ["subscribe", "publish"] }) }
|
311
|
-
let(:upgraded_token_cb) {
|
311
|
+
let(:upgraded_token_cb) { lambda do |token_params|
|
312
312
|
rest_client.auth.create_token_request({ capability: upgraded_capability })
|
313
313
|
end }
|
314
|
-
let(:identified_token_cb) {
|
314
|
+
let(:identified_token_cb) { lambda do |token_params|
|
315
315
|
rest_client.auth.create_token_request({ client_id: 'bob' })
|
316
316
|
end }
|
317
317
|
let(:downgraded_capability) { JSON.dump({ "bar" => ["subscribe"] }) }
|
318
|
-
let(:downgraded_token_cb) {
|
318
|
+
let(:downgraded_token_cb) { lambda do |token_params|
|
319
319
|
rest_client.auth.create_token_request({ capability: downgraded_capability })
|
320
320
|
end }
|
321
321
|
|
@@ -589,7 +589,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
589
589
|
context 'when client is identified' do
|
590
590
|
let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) }
|
591
591
|
|
592
|
-
let(:basic_token_cb) {
|
592
|
+
let(:basic_token_cb) { lambda do |token_params|
|
593
593
|
rest_client.auth.create_token_request({ client_id: 'mike', capability: basic_capability })
|
594
594
|
end }
|
595
595
|
|
@@ -607,16 +607,17 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
607
607
|
|
608
608
|
context 'when auth fails' do
|
609
609
|
let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) }
|
610
|
+
let!(:token_string) { client.rest_client.auth.request_token.token }
|
610
611
|
|
611
612
|
it 'transitions the connection state to the FAILED state (#RSA15c, #RTC8a2, #RTC8a3)' do
|
612
613
|
connection_failed = false
|
613
614
|
|
614
615
|
client.connection.once(:connected) do
|
615
|
-
client.auth.authorize(nil, auth_callback:
|
616
|
+
client.auth.authorize(nil, auth_callback: lambda { |token_params| "#{app_id}.invalid.token.will.cause.failure" }).tap do |deferrable|
|
616
617
|
deferrable.errback do |error|
|
617
618
|
EventMachine.add_timer(0.2) do
|
618
619
|
expect(connection_failed).to eql(true)
|
619
|
-
expect(error.message).to match(/
|
620
|
+
expect(error.message).to match(/invalid.*accessToken/i)
|
620
621
|
expect(error.code).to eql(40005)
|
621
622
|
stop_reactor
|
622
623
|
end
|
@@ -626,7 +627,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
626
627
|
end
|
627
628
|
|
628
629
|
client.connection.once(:failed) do
|
629
|
-
expect(client.connection.error_reason.message).to match(/
|
630
|
+
expect(client.connection.error_reason.message).to match(/invalid.*accessToken/i)
|
630
631
|
expect(client.connection.error_reason.code).to eql(40005)
|
631
632
|
connection_failed = true
|
632
633
|
end
|
@@ -638,7 +639,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
638
639
|
|
639
640
|
it 'calls the error callback of authorize and leaves the connection intact (#RSA4c3)' do
|
640
641
|
client.connection.once(:connected) do
|
641
|
-
client.auth.authorize(nil, auth_callback:
|
642
|
+
client.auth.authorize(nil, auth_callback: lambda { |token_params| raise 'Exception raised' }).errback do |error|
|
642
643
|
EventMachine.add_timer(0.2) do
|
643
644
|
expect(connection).to be_connected
|
644
645
|
expect(error.message).to match(/Exception raised/i)
|
@@ -660,17 +661,19 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
660
661
|
client.connection.once(:disconnected) { raise 'Upgrade does not require a disconnect' }
|
661
662
|
|
662
663
|
channel = client.channels.get('foo')
|
663
|
-
channel.
|
664
|
-
|
665
|
-
|
664
|
+
channel.attach do
|
665
|
+
channel.publish('not-allowed').errback do |error|
|
666
|
+
expect(error.code).to eql(40160)
|
667
|
+
expect(error.message).to match(/permission denied/)
|
666
668
|
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
669
|
+
client.auth.authorize(nil, auth_callback: upgraded_token_cb)
|
670
|
+
client.connection.once(:update) do
|
671
|
+
expect(client.connection.error_reason).to be_nil
|
672
|
+
channel.subscribe('now-allowed') do |message|
|
673
|
+
stop_reactor
|
674
|
+
end
|
675
|
+
channel.publish 'now-allowed'
|
672
676
|
end
|
673
|
-
channel.publish 'now-allowed'
|
674
677
|
end
|
675
678
|
end
|
676
679
|
end
|
@@ -749,11 +752,8 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
749
752
|
end
|
750
753
|
|
751
754
|
context 'when received' do
|
752
|
-
# Ably
|
753
|
-
|
754
|
-
# In local env, that window is 5 seconds instead of 30 seconds
|
755
|
-
let(:local_offset) { ENV['ABLY_ENV'] == 'local' ? 25 : 0 }
|
756
|
-
let(:client_options) { default_options.merge(use_token_auth: :true, default_token_params: { ttl: 33 - local_offset }) }
|
755
|
+
# Ably will send AUTH 30 seconds before expiry
|
756
|
+
let(:client_options) { default_options.merge(use_token_auth: :true, default_token_params: { ttl: 33 }) }
|
757
757
|
|
758
758
|
it 'should immediately start a new authentication process (#RTN22)' do
|
759
759
|
client.connection.once(:connected) do
|
@@ -1014,44 +1014,248 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
1014
1014
|
end
|
1015
1015
|
end
|
1016
1016
|
|
1017
|
-
context 'deprecated #authorise' do
|
1017
|
+
context 'deprecated #authorise', :prevent_log_stubbing do
|
1018
1018
|
let(:client_options) { default_options.merge(key: api_key, logger: custom_logger_object, use_token_auth: true) }
|
1019
|
-
let(:
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1019
|
+
let(:custom_logger_object) { TestLogger.new }
|
1020
|
+
|
1021
|
+
it 'logs a deprecation warning (#RSA10l)' do
|
1022
|
+
client.auth.authorise
|
1023
|
+
expect(custom_logger_object.logs.find { |severity, message| message.match(/authorise.*deprecated/i)} ).to_not be_nil
|
1024
|
+
stop_reactor
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
it 'returns a valid token (#RSA10l)' do
|
1028
|
+
client.auth.authorise do |response|
|
1029
|
+
expect(response).to be_a(Ably::Models::TokenDetails)
|
1030
|
+
stop_reactor
|
1031
|
+
end
|
1032
|
+
end
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
context 'when using JWT' do
|
1036
|
+
let(:auth_url) { 'https://echo.ably.io/createJWT' }
|
1037
|
+
let(:auth_params) { { keyName: key_name, keySecret: key_secret } }
|
1038
|
+
let(:channel_name) { "test_JWT_#{random_str}" }
|
1039
|
+
let(:message_name) { 'message_JWT' }
|
1040
|
+
|
1041
|
+
# RSA8g
|
1042
|
+
context 'when using auth_url' do
|
1043
|
+
let(:client_options) { default_options.merge(auth_url: auth_url, auth_params: auth_params) }
|
1044
|
+
|
1045
|
+
context 'when credentials are valid' do
|
1046
|
+
it 'client successfully fetches a channel and publishes a message' do
|
1047
|
+
channel = client.channels.get(channel_name)
|
1048
|
+
channel.subscribe do |message|
|
1049
|
+
expect(message.name).to eql(message_name)
|
1050
|
+
stop_reactor
|
1051
|
+
end
|
1052
|
+
channel.publish message_name
|
1023
1053
|
end
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
context 'when credentials are wrong' do
|
1057
|
+
let(:auth_params) { { keyName: key_name, keySecret: 'invalid' } }
|
1024
1058
|
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1059
|
+
it 'disconnected includes and invalid signature message' do
|
1060
|
+
client.connection.once(:disconnected) do |state_change|
|
1061
|
+
expect(state_change.reason.message.match(/signature verification failed/i)).to_not be_nil
|
1062
|
+
expect(state_change.reason.code).to eql(40144)
|
1063
|
+
stop_reactor
|
1028
1064
|
end
|
1065
|
+
client.connect
|
1029
1066
|
end
|
1067
|
+
end
|
1030
1068
|
|
1031
|
-
|
1032
|
-
|
1069
|
+
context 'when token is expired' do
|
1070
|
+
let(:token_duration) { 5 }
|
1071
|
+
let(:auth_params) { { keyName: key_name, keySecret: key_secret, expiresIn: token_duration } }
|
1072
|
+
it 'receives a 40142 error from the server' do
|
1073
|
+
client.connection.once(:connected) do
|
1074
|
+
client.connection.once(:disconnected) do |state_change|
|
1075
|
+
expect(state_change.reason).to be_a(Ably::Models::ErrorInfo)
|
1076
|
+
expect(state_change.reason.message).to match(/(expire)/i)
|
1077
|
+
expect(state_change.reason.code).to eql(40142)
|
1078
|
+
stop_reactor
|
1079
|
+
end
|
1080
|
+
end
|
1033
1081
|
end
|
1082
|
+
end
|
1083
|
+
end
|
1034
1084
|
|
1035
|
-
|
1036
|
-
|
1085
|
+
# RSA8g
|
1086
|
+
context 'when using auth_callback' do
|
1087
|
+
let(:token_callback) do
|
1088
|
+
lambda do |token_params|
|
1089
|
+
Ably::Rest::Client.new(default_options).auth.request_token({}, { auth_url: auth_url, auth_params: auth_params }).token
|
1037
1090
|
end
|
1091
|
+
end
|
1092
|
+
let(:client_options) { default_options.merge(auth_callback: token_callback) }
|
1093
|
+
WebMock.allow_net_connect!
|
1094
|
+
WebMock.disable!
|
1095
|
+
context 'when credentials are valid' do
|
1096
|
+
|
1097
|
+
it 'authentication succeeds and client can post a message' do
|
1098
|
+
channel = client.channels.get(channel_name)
|
1099
|
+
channel.subscribe do |message|
|
1100
|
+
expect(message.name).to eql(message_name)
|
1101
|
+
stop_reactor
|
1102
|
+
end
|
1103
|
+
channel.publish(message_name) do
|
1104
|
+
# assert_requested :get, Addressable::Template.new("#{auth_url}{?keyName,keySecret}")
|
1105
|
+
end
|
1106
|
+
end
|
1107
|
+
end
|
1038
1108
|
|
1039
|
-
|
1109
|
+
context 'when credentials are invalid' do
|
1110
|
+
let(:auth_params) { { keyName: key_name, keySecret: 'invalid' } }
|
1111
|
+
|
1112
|
+
it 'authentication fails and reason for disconnection is invalid signature' do
|
1113
|
+
client.connection.once(:disconnected) do |state_change|
|
1114
|
+
expect(state_change.reason.message.match(/signature verification failed/i)).to_not be_nil
|
1115
|
+
expect(state_change.reason.code).to eql(40144)
|
1116
|
+
stop_reactor
|
1117
|
+
end
|
1118
|
+
client.connect
|
1040
1119
|
end
|
1041
1120
|
end
|
1042
1121
|
end
|
1043
|
-
let(:custom_logger_object) { custom_logger.new }
|
1044
1122
|
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1123
|
+
context 'when the client is initialized with ClientOptions and the token is a JWT token' do
|
1124
|
+
let(:client_options) { { token: token, environment: environment, protocol: protocol } }
|
1125
|
+
|
1126
|
+
context 'when credentials are valid' do
|
1127
|
+
let(:token) { Faraday.get("#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}").body }
|
1128
|
+
|
1129
|
+
it 'posts successfully to a channel' do
|
1130
|
+
channel = client.channels.get(channel_name)
|
1131
|
+
channel.subscribe do |message|
|
1132
|
+
expect(message.name).to eql(message_name)
|
1133
|
+
stop_reactor
|
1134
|
+
end
|
1135
|
+
channel.publish(message_name)
|
1136
|
+
end
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
context 'when credentials are invalid' do
|
1140
|
+
let(:key_secret) { 'invalid' }
|
1141
|
+
let(:token) { Faraday.get("#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}").body }
|
1142
|
+
let(:client_options) { { token: token, environment: environment, protocol: protocol, log_level: :none } }
|
1143
|
+
|
1144
|
+
it 'fails with an invalid signature error' do
|
1145
|
+
client.connection.once(:disconnected) do |state_change|
|
1146
|
+
expect(state_change.reason.message.match(/signature verification failed/i)).to_not be_nil
|
1147
|
+
expect(state_change.reason.code).to eql(40144)
|
1148
|
+
stop_reactor
|
1149
|
+
end
|
1150
|
+
client.connect
|
1151
|
+
end
|
1152
|
+
end
|
1049
1153
|
end
|
1050
1154
|
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1155
|
+
context 'when JWT token expires' do
|
1156
|
+
before do
|
1157
|
+
stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', 0 # allow token to be used even if about to expire
|
1158
|
+
stub_const 'Ably::Auth::TOKEN_DEFAULTS', Ably::Auth::TOKEN_DEFAULTS.merge(renew_token_buffer: 0) # Ensure tokens issued expire immediately after issue
|
1159
|
+
end
|
1160
|
+
let(:token_callback) do
|
1161
|
+
lambda do |token_params|
|
1162
|
+
# Ably in all environments other than production will send AUTH 5 seconds before expiry, so
|
1163
|
+
# we generate a JWT that expires in 5s so that the window for Realtime to send has passed
|
1164
|
+
tokenResponse = Faraday.get "#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}&expiresIn=5"
|
1165
|
+
tokenResponse.body
|
1166
|
+
end
|
1167
|
+
end
|
1168
|
+
let(:client_options) { default_options.merge(use_token_auth: true, auth_callback: token_callback) }
|
1169
|
+
|
1170
|
+
# RTC8a
|
1171
|
+
it 'client disconnects, a new token is requested via auth_callback and the client gets reconnected' do
|
1172
|
+
client.connection.once(:connected) do
|
1173
|
+
original_token = auth.current_token_details
|
1174
|
+
original_conn_id = client.connection.id
|
1175
|
+
|
1176
|
+
client.connection.once(:disconnected) do |state_change|
|
1177
|
+
expect(state_change.reason.code).to eql(40142)
|
1178
|
+
|
1179
|
+
client.connection.once(:connected) do
|
1180
|
+
expect(original_token).to_not eql(auth.current_token_details)
|
1181
|
+
expect(original_conn_id).to eql(client.connection.id)
|
1182
|
+
stop_reactor
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
end
|
1186
|
+
end
|
1187
|
+
|
1188
|
+
context 'and an AUTH procol message is received' do
|
1189
|
+
let(:token_callback) do
|
1190
|
+
lambda do |token_params|
|
1191
|
+
# Ably in all environments other than local will send AUTH 30 seconds before expiry
|
1192
|
+
# We set the TTL to 35s so there's room to receive an AUTH protocol message
|
1193
|
+
tokenResponse = Faraday.get "#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}&expiresIn=35"
|
1194
|
+
tokenResponse.body
|
1195
|
+
end
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
# RTC8a, RTC8a4
|
1199
|
+
it 'client reauths correctly without going through a disconnection' do
|
1200
|
+
client.connection.once(:connected) do
|
1201
|
+
original_token = client.auth.current_token_details
|
1202
|
+
received_auth = false
|
1203
|
+
|
1204
|
+
client.connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
|
1205
|
+
received_auth = true if protocol_message.action == :auth
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
client.connection.once(:update) do
|
1209
|
+
expect(received_auth).to be_truthy
|
1210
|
+
expect(original_token).to_not eql(client.auth.current_token_details)
|
1211
|
+
stop_reactor
|
1212
|
+
end
|
1213
|
+
end
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
end
|
1217
|
+
|
1218
|
+
context 'when the JWT token request includes a client_id' do
|
1219
|
+
let(:client_id) { random_str }
|
1220
|
+
let(:auth_callback) do
|
1221
|
+
lambda do |token_params|
|
1222
|
+
Faraday.get("#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}&client_id=#{client_id}").body
|
1223
|
+
end
|
1224
|
+
end
|
1225
|
+
let(:client_options) { default_options.merge(auth_callback: auth_callback) }
|
1226
|
+
|
1227
|
+
it 'the client_id is the same that was specified in the auth_callback that generated the JWT token' do
|
1228
|
+
client.connection.once(:connected) do
|
1229
|
+
expect(client.auth.client_id).to eql(client_id)
|
1230
|
+
stop_reactor
|
1231
|
+
end
|
1232
|
+
end
|
1233
|
+
end
|
1234
|
+
|
1235
|
+
context 'when the JWT token request includes a subscribe-only capability' do
|
1236
|
+
let(:channel_with_publish_permissions) { "test_JWT_with_publish_#{random_str}" }
|
1237
|
+
let(:basic_capability) { JSON.dump(channel_name => ['subscribe'], channel_with_publish_permissions => ['publish']) }
|
1238
|
+
let(:auth_callback) do
|
1239
|
+
lambda do |token_params|
|
1240
|
+
Faraday.get("#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}&capability=#{URI.escape(basic_capability)}").body
|
1241
|
+
end
|
1242
|
+
end
|
1243
|
+
let(:client_options) { default_options.merge(auth_callback: auth_callback, log_level: :error) }
|
1244
|
+
|
1245
|
+
it 'client fails to publish to a channel with subscribe-only capability and publishes successfully on a channel with permissions' do
|
1246
|
+
client.connection.once(:connected) do
|
1247
|
+
forbidden_channel = client.channels.get(channel_name)
|
1248
|
+
allowed_channel = client.channels.get(channel_with_publish_permissions)
|
1249
|
+
forbidden_channel.publish('not-allowed').errback do |error|
|
1250
|
+
expect(error.code).to eql(40160)
|
1251
|
+
expect(error.message).to match(/permission denied/)
|
1252
|
+
|
1253
|
+
allowed_channel.publish(message_name) do |message|
|
1254
|
+
expect(message.name).to eql(message_name)
|
1255
|
+
stop_reactor
|
1256
|
+
end
|
1257
|
+
end
|
1258
|
+
end
|
1055
1259
|
end
|
1056
1260
|
end
|
1057
1261
|
end
|