ably-rest 1.0.6 → 1.1.4.rc
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/CHANGELOG.md +1 -1
- data/README.md +23 -15
- data/ably-rest.gemspec +6 -6
- data/lib/submodules/ably-ruby/.editorconfig +14 -0
- data/lib/submodules/ably-ruby/.travis.yml +10 -8
- data/lib/submodules/ably-ruby/CHANGELOG.md +75 -3
- 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 +15 -10
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +30 -4
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +10 -4
- data/lib/submodules/ably-ruby/lib/ably/logger.rb +7 -1
- 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 +4 -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/conversions.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +1 -1
- 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/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 +1 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +24 -102
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +2 -6
- 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 +6 -2
- 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 +34 -20
- 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 +1 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +4 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +3 -3
- 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 +53 -17
- data/lib/submodules/ably-ruby/lib/ably/rest/channel/push_channel.rb +62 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +162 -35
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +4 -1
- 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 +245 -17
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +26 -20
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +177 -59
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +153 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +72 -6
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +129 -18
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +36 -34
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +201 -167
- 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 +41 -3
- 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 +129 -10
- 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/spec_helper.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +9 -5
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +2 -2
- data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +10 -3
- 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/enum_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 +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 +8 -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
- metadata +46 -21
@@ -43,25 +43,38 @@ module Ably::Realtime
|
|
43
43
|
end
|
44
44
|
|
45
45
|
unless client.auth.authentication_security_requirements_met?
|
46
|
-
connection.transition_state_machine :failed, reason: Ably::Exceptions::InsecureRequest.new('Cannot use Basic Auth over non-TLS connections', 401,
|
46
|
+
connection.transition_state_machine :failed, reason: Ably::Exceptions::InsecureRequest.new('Cannot use Basic Auth over non-TLS connections', 401, Ably::Exceptions::Codes::INVALID_USE_OF_BASIC_AUTH_OVER_NONTLS_TRANSPORT)
|
47
47
|
return
|
48
48
|
end
|
49
49
|
|
50
50
|
logger.debug { 'ConnectionManager: Opening a websocket transport connection' }
|
51
51
|
|
52
|
+
# The socket attempt can fail at the same time as a timer firing so ensure
|
53
|
+
# only one outcome is processed from this setup attempt
|
54
|
+
setup_attempt_status = {}
|
55
|
+
setup_failed = lambda do
|
56
|
+
return true if setup_attempt_status[:failed]
|
57
|
+
setup_attempt_status[:failed] = true
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
52
61
|
connection.create_websocket_transport.tap do |socket_deferrable|
|
53
62
|
socket_deferrable.callback do |websocket_transport|
|
54
63
|
subscribe_to_transport_events websocket_transport
|
55
64
|
yield websocket_transport if block_given?
|
56
65
|
end
|
57
66
|
socket_deferrable.errback do |error|
|
67
|
+
next if setup_failed.call
|
58
68
|
connection_opening_failed error
|
59
69
|
end
|
60
70
|
end
|
61
71
|
|
72
|
+
# The connection request timeout must be marginally higher than the REST request timeout to ensure
|
73
|
+
# any HTTP auth request failure due to timeout triggers before the connection timer kicks in
|
62
74
|
logger.debug { "ConnectionManager: Setting up automatic connection timeout timer for #{realtime_request_timeout}s" }
|
63
75
|
create_timeout_timer_whilst_in_state(:connecting, realtime_request_timeout) do
|
64
|
-
|
76
|
+
next if setup_failed.call
|
77
|
+
connection_opening_failed Ably::Exceptions::ConnectionTimeout.new("Connection to Ably timed out after #{realtime_request_timeout}s", nil, Ably::Exceptions::Codes::CONNECTION_TIMED_OUT)
|
65
78
|
end
|
66
79
|
end
|
67
80
|
|
@@ -72,7 +85,7 @@ module Ably::Realtime
|
|
72
85
|
if error.kind_of?(Ably::Exceptions::BaseAblyException)
|
73
86
|
# Authentication errors that indicate the authentication failure is terminal should move to the failed state
|
74
87
|
if ([401, 403].include?(error.status) && !RESOLVABLE_ERROR_CODES.fetch(:token_expired).include?(error.code)) ||
|
75
|
-
(error.code == Ably::Exceptions::INVALID_CLIENT_ID)
|
88
|
+
(error.code == Ably::Exceptions::Codes::INVALID_CLIENT_ID)
|
76
89
|
connection.transition_state_machine :failed, reason: error
|
77
90
|
return
|
78
91
|
end
|
@@ -80,7 +93,12 @@ module Ably::Realtime
|
|
80
93
|
|
81
94
|
logger.warn { "ConnectionManager: Connection to #{connection.current_host}:#{connection.port} failed; #{error.message}" }
|
82
95
|
next_state = get_next_retry_state_info
|
83
|
-
|
96
|
+
|
97
|
+
if connection.state == next_state.fetch(:state)
|
98
|
+
logger.error { "ConnectionManager: Skipping next retry state after connection opening failed as already in state #{next_state}\n#{caller[0..20].join("\n")}" }
|
99
|
+
else
|
100
|
+
connection.transition_state_machine next_state.fetch(:state), retry_in: next_state.fetch(:pause), reason: Ably::Exceptions::ConnectionError.new("Connection failed: #{error.message}", nil, Ably::Exceptions::Codes::CONNECTION_FAILED, error)
|
101
|
+
end
|
84
102
|
end
|
85
103
|
|
86
104
|
# Called whenever a new connection is made
|
@@ -100,13 +118,11 @@ module Ably::Realtime
|
|
100
118
|
resend_pending_message_ack_queue
|
101
119
|
else
|
102
120
|
logger.debug { "ConnectionManager: Connection was not resumed, old connection ID #{connection.id} has been updated with new connection ID #{protocol_message.connection_id} and key #{protocol_message.connection_key}" }
|
103
|
-
connection.reset_client_serial
|
104
121
|
nack_messages_on_all_channels protocol_message.error
|
105
122
|
force_reattach_on_channels protocol_message.error
|
106
123
|
end
|
107
124
|
else
|
108
125
|
logger.debug { "ConnectionManager: New connection created with ID #{protocol_message.connection_id} and key #{protocol_message.connection_key}" }
|
109
|
-
connection.reset_client_serial
|
110
126
|
end
|
111
127
|
|
112
128
|
reattach_suspended_channels protocol_message.error
|
@@ -319,7 +335,7 @@ module Ably::Realtime
|
|
319
335
|
@liveness_timer = EventMachine::Timer.new(connection.heartbeat_interval + 0.1) do
|
320
336
|
if connection.connected? && (connection.time_since_connection_confirmed_alive? >= connection.heartbeat_interval)
|
321
337
|
msg = "No activity seen from realtime in #{connection.heartbeat_interval}; assuming connection has dropped";
|
322
|
-
error = Ably::Exceptions::ConnectionTimeout.new(msg,
|
338
|
+
error = Ably::Exceptions::ConnectionTimeout.new(msg, Ably::Exceptions::Codes::DISCONNECTED, 408)
|
323
339
|
connection.transition_state_machine! :disconnected, reason: error
|
324
340
|
end
|
325
341
|
end
|
@@ -492,9 +508,9 @@ module Ably::Realtime
|
|
492
508
|
connection.transition_state_machine :closed
|
493
509
|
elsif !connection.closed? && !connection.disconnected? && !connection.failed? && !connection.suspended?
|
494
510
|
exception = if reason
|
495
|
-
Ably::Exceptions::TransportClosed.new(reason, nil,
|
511
|
+
Ably::Exceptions::TransportClosed.new(reason, nil, Ably::Exceptions::Codes::DISCONNECTED)
|
496
512
|
else
|
497
|
-
Ably::Exceptions::TransportClosed.new('Transport disconnected unexpectedly', nil,
|
513
|
+
Ably::Exceptions::TransportClosed.new('Transport disconnected unexpectedly', nil, Ably::Exceptions::Codes::DISCONNECTED)
|
498
514
|
end
|
499
515
|
next_state = get_next_retry_state_info
|
500
516
|
connection.transition_state_machine next_state.fetch(:state), retry_in: next_state.fetch(:pause), reason: exception
|
@@ -157,7 +157,7 @@ module Ably::Realtime
|
|
157
157
|
logger.debug { "WebsocketTransport: Prot msg recv <=: #{action_name} - #{event_data}" }
|
158
158
|
|
159
159
|
if protocol_message.invalid?
|
160
|
-
error = Ably::Exceptions::ProtocolError.new("Invalid Protocol Message received: #{event_data}\nConnection moving to the failed state as the protocol is invalid and unsupported", 400,
|
160
|
+
error = Ably::Exceptions::ProtocolError.new("Invalid Protocol Message received: #{event_data}\nConnection moving to the failed state as the protocol is invalid and unsupported", 400, Ably::Exceptions::Codes::PROTOCOL_ERROR)
|
161
161
|
logger.fatal { "WebsocketTransport: #{error.message}" }
|
162
162
|
failed_protocol_message = Ably::Models::ProtocolMessage.new(
|
163
163
|
action: Ably::Models::ProtocolMessage::ACTION.Error,
|
@@ -233,7 +233,7 @@ module Ably::Realtime
|
|
233
233
|
deferrable.fail Ably::Exceptions::InvalidState.new(
|
234
234
|
'Presence state is out of sync as channel is SUSPENDED. Presence#get on a SUSPENDED channel is only supported with option wait_for_sync: false',
|
235
235
|
nil,
|
236
|
-
|
236
|
+
Ably::Exceptions::Codes::PRESENCE_STATE_IS_OUT_OF_SYNC
|
237
237
|
)
|
238
238
|
end
|
239
239
|
return deferrable
|
@@ -330,7 +330,7 @@ module Ably::Realtime
|
|
330
330
|
def send_presence_protocol_message(presence_action, client_id, data)
|
331
331
|
presence_message = create_presence_message(presence_action, client_id, data)
|
332
332
|
unless presence_message.client_id
|
333
|
-
raise Ably::Exceptions::Standard.new('Unable to enter create presence message without a client_id', 400,
|
333
|
+
raise Ably::Exceptions::Standard.new('Unable to enter create presence message without a client_id', 400, Ably::Exceptions::Codes::UNABLE_TO_ENTER_PRESENCE_CHANNEL_NO_CLIENTID)
|
334
334
|
end
|
335
335
|
|
336
336
|
protocol_message = {
|
@@ -437,13 +437,13 @@ module Ably::Realtime
|
|
437
437
|
|
438
438
|
def attach_channel_then(deferrable)
|
439
439
|
if channel.detached? || channel.failed?
|
440
|
-
deferrable.fail Ably::Exceptions::InvalidState.new("Operation is not allowed when channel is in #{channel.state}", 400,
|
440
|
+
deferrable.fail Ably::Exceptions::InvalidState.new("Operation is not allowed when channel is in #{channel.state}", 400, Ably::Exceptions::Codes::UNABLE_TO_ENTER_PRESENCE_CHANNEL_INVALID_CHANNEL_STATE)
|
441
441
|
else
|
442
442
|
channel.unsafe_once(:attached, :detached, :failed) do |channel_state_change|
|
443
443
|
if channel_state_change.current == :attached
|
444
444
|
yield
|
445
445
|
else
|
446
|
-
deferrable.fail Ably::Exceptions::InvalidState.new("Operation failed as channel transitioned to #{channel_state_change.current}", 400,
|
446
|
+
deferrable.fail Ably::Exceptions::InvalidState.new("Operation failed as channel transitioned to #{channel_state_change.current}", 400, Ably::Exceptions::Codes::UNABLE_TO_ENTER_PRESENCE_CHANNEL_INVALID_CHANNEL_STATE)
|
447
447
|
end
|
448
448
|
end
|
449
449
|
channel.attach
|
@@ -275,7 +275,7 @@ module Ably::Realtime
|
|
275
275
|
presence_message_client_id = presence_message.client_id || client.auth.client_id
|
276
276
|
re_enter_error = Ably::Models::ErrorInfo.new(
|
277
277
|
message: "unable to automatically re-enter presence channel for client_id '#{presence_message_client_id}'. Source error code #{error.code} and message '#{error.message}'",
|
278
|
-
code:
|
278
|
+
code: Ably::Exceptions::Codes::UNABLE_TO_AUTOMATICALLY_REENTER_PRESENCE_CHANNEL
|
279
279
|
)
|
280
280
|
channel.emit :update, Ably::Models::ChannelStateChange.new(
|
281
281
|
current: channel.state,
|
@@ -312,14 +312,14 @@ module Ably::Realtime
|
|
312
312
|
when Ably::Models::PresenceMessage::ACTION.Leave
|
313
313
|
remove_presence_member presence_message
|
314
314
|
else
|
315
|
-
Ably::Exceptions::ProtocolError.new("Protocol error, unknown presence action #{presence_message.action}", 400,
|
315
|
+
Ably::Exceptions::ProtocolError.new("Protocol error, unknown presence action #{presence_message.action}", 400, Ably::Exceptions::Codes::PROTOCOL_ERROR)
|
316
316
|
end
|
317
317
|
end
|
318
318
|
|
319
319
|
def ensure_presence_message_is_valid(presence_message)
|
320
320
|
return true if presence_message.connection_id
|
321
321
|
|
322
|
-
error = Ably::Exceptions::ProtocolError.new("Protocol error, presence message is missing connectionId", 400,
|
322
|
+
error = Ably::Exceptions::ProtocolError.new("Protocol error, presence message is missing connectionId", 400, Ably::Exceptions::Codes::PROTOCOL_ERROR)
|
323
323
|
logger.error { "PresenceMap: On channel '#{channel.name}' error: #{error}" }
|
324
324
|
end
|
325
325
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'ably/realtime/push/admin'
|
2
|
+
|
3
|
+
module Ably
|
4
|
+
module Realtime
|
5
|
+
# Class providing push notification functionality
|
6
|
+
class Push
|
7
|
+
# @private
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
def initialize(client)
|
11
|
+
@client = client
|
12
|
+
end
|
13
|
+
|
14
|
+
# Admin features for push notifications like managing devices and channel subscriptions
|
15
|
+
# @return [Ably::Realtime::Push::Admin]
|
16
|
+
def admin
|
17
|
+
@admin ||= Admin.new(self)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Activate this device for push notifications by registering with the push transport such as GCM/APNS
|
21
|
+
#
|
22
|
+
# @note This is unsupported in the Ruby library
|
23
|
+
def activate(*arg)
|
24
|
+
raise_unsupported
|
25
|
+
end
|
26
|
+
|
27
|
+
# Deactivate this device for push notifications by removing the registration with the push transport such as GCM/APNS
|
28
|
+
#
|
29
|
+
# @note This is unsupported in the Ruby library
|
30
|
+
def deactivate(*arg)
|
31
|
+
raise_unsupported
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def raise_unsupported
|
36
|
+
raise Ably::Exceptions::PushNotificationsNotSupported, 'This device does not support receiving or subscribing to push notifications. All PushChannel methods are unavailable'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'ably/realtime/push/device_registrations'
|
2
|
+
require 'ably/realtime/push/channel_subscriptions'
|
3
|
+
|
4
|
+
module Ably::Realtime
|
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::AsyncWrapper
|
10
|
+
include Ably::Modules::Conversions
|
11
|
+
|
12
|
+
# @api private
|
13
|
+
attr_reader :client
|
14
|
+
|
15
|
+
# @api private
|
16
|
+
attr_reader :push
|
17
|
+
|
18
|
+
def initialize(push)
|
19
|
+
@push = push
|
20
|
+
@client = push.client
|
21
|
+
end
|
22
|
+
|
23
|
+
# (see Ably::Rest::Push#publish)
|
24
|
+
#
|
25
|
+
# @yield Block is invoked upon successful publish of the message
|
26
|
+
# @return [Ably::Util::SafeDeferrable]
|
27
|
+
#
|
28
|
+
def publish(recipient, data, &callback)
|
29
|
+
raise ArgumentError, "Expecting a Hash object for recipient, got #{recipient.class}" unless recipient.kind_of?(Hash)
|
30
|
+
raise ArgumentError, "Recipient data is empty. You must provide recipient details" if recipient.empty?
|
31
|
+
raise ArgumentError, "Expecting a Hash object for data, got #{data.class}" unless data.kind_of?(Hash)
|
32
|
+
raise ArgumentError, "Push data field is empty. You must provide attributes for the push notification" if data.empty?
|
33
|
+
|
34
|
+
async_wrap(callback) do
|
35
|
+
rest_push_admin.publish(recipient, data)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Manage device registrations
|
40
|
+
# @return [Ably::Realtime::Push::DeviceRegistrations]
|
41
|
+
def device_registrations
|
42
|
+
@device_registrations ||= DeviceRegistrations.new(self)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Manage channel subscriptions for devices or clients
|
46
|
+
# @return [Ably::Realtime::Push::ChannelSubscriptions]
|
47
|
+
def channel_subscriptions
|
48
|
+
@channel_subscriptions ||= ChannelSubscriptions.new(self)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def rest_push_admin
|
53
|
+
client.rest_client.push.admin
|
54
|
+
end
|
55
|
+
|
56
|
+
def logger
|
57
|
+
client.logger
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Ably::Realtime
|
2
|
+
class Push
|
3
|
+
# Manage push notification channel subscriptions for devices or clients
|
4
|
+
class ChannelSubscriptions
|
5
|
+
include Ably::Modules::Conversions
|
6
|
+
include Ably::Modules::AsyncWrapper
|
7
|
+
|
8
|
+
# @api private
|
9
|
+
attr_reader :client
|
10
|
+
|
11
|
+
# @api private
|
12
|
+
attr_reader :admin
|
13
|
+
|
14
|
+
def initialize(admin)
|
15
|
+
@admin = admin
|
16
|
+
@client = admin.client
|
17
|
+
end
|
18
|
+
|
19
|
+
# (see Ably::Rest::Push::ChannelSubscriptions#list)
|
20
|
+
#
|
21
|
+
# @yield Block is invoked when request succeeds
|
22
|
+
# @return [Ably::Util::SafeDeferrable]
|
23
|
+
#
|
24
|
+
def list(params, &callback)
|
25
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
26
|
+
|
27
|
+
if (IdiomaticRubyWrapper(params).keys & [:channel, :client_id, :device_id]).length == 0
|
28
|
+
raise ArgumentError, "at least one channel, client_id or device_id filter param must be provided"
|
29
|
+
end
|
30
|
+
|
31
|
+
async_wrap(callback) do
|
32
|
+
rest_channel_subscriptions.list(params.merge(async_blocking_operations: true))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# (see Ably::Rest::Push::ChannelSubscriptions#list_channels)
|
37
|
+
#
|
38
|
+
# @yield Block is invoked when request succeeds
|
39
|
+
# @return [Ably::Util::SafeDeferrable]
|
40
|
+
#
|
41
|
+
def list_channels(params = {}, &callback)
|
42
|
+
params = {} if params.nil?
|
43
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
44
|
+
|
45
|
+
async_wrap(callback) do
|
46
|
+
rest_channel_subscriptions.list_channels(params.merge(async_blocking_operations: true))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# (see Ably::Rest::Push::ChannelSubscriptions#save)
|
51
|
+
#
|
52
|
+
# @yield Block is invoked when request succeeds
|
53
|
+
# @return [Ably::Util::SafeDeferrable]
|
54
|
+
#
|
55
|
+
def save(push_channel_subscription, &callback)
|
56
|
+
push_channel_subscription_object = PushChannelSubscription(push_channel_subscription)
|
57
|
+
raise ArgumentError, "Channel is required yet is empty" if push_channel_subscription_object.channel.to_s.empty?
|
58
|
+
|
59
|
+
async_wrap(callback) do
|
60
|
+
rest_channel_subscriptions.save(push_channel_subscription)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# (see Ably::Rest::Push::ChannelSubscriptions#remove)
|
65
|
+
#
|
66
|
+
# @yield Block is invoked when request succeeds
|
67
|
+
# @return [Ably::Util::SafeDeferrable]
|
68
|
+
#
|
69
|
+
def remove(push_channel_subscription, &callback)
|
70
|
+
push_channel_subscription_object = PushChannelSubscription(push_channel_subscription)
|
71
|
+
raise ArgumentError, "Channel is required yet is empty" if push_channel_subscription_object.channel.to_s.empty?
|
72
|
+
if push_channel_subscription_object.client_id.to_s.empty? && push_channel_subscription_object.device_id.to_s.empty?
|
73
|
+
raise ArgumentError, "Either client_id or device_id must be present"
|
74
|
+
end
|
75
|
+
|
76
|
+
async_wrap(callback) do
|
77
|
+
rest_channel_subscriptions.remove(push_channel_subscription)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# (see Ably::Rest::Push::ChannelSubscriptions#remove_where)
|
82
|
+
#
|
83
|
+
# @yield Block is invoked when request succeeds
|
84
|
+
# @return [Ably::Util::SafeDeferrable]
|
85
|
+
#
|
86
|
+
def remove_where(params, &callback)
|
87
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
88
|
+
|
89
|
+
if (IdiomaticRubyWrapper(params).keys & [:channel, :client_id, :device_id]).length == 0
|
90
|
+
raise ArgumentError, "at least one channel, client_id or device_id filter param must be provided"
|
91
|
+
end
|
92
|
+
|
93
|
+
async_wrap(callback) do
|
94
|
+
rest_channel_subscriptions.remove_where(params)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def rest_channel_subscriptions
|
100
|
+
client.rest_client.push.admin.channel_subscriptions
|
101
|
+
end
|
102
|
+
|
103
|
+
def logger
|
104
|
+
client.logger
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Ably::Realtime
|
2
|
+
class Push
|
3
|
+
# Manage device registrations for push notifications
|
4
|
+
class DeviceRegistrations
|
5
|
+
include Ably::Modules::Conversions
|
6
|
+
include Ably::Modules::AsyncWrapper
|
7
|
+
|
8
|
+
# @api private
|
9
|
+
attr_reader :client
|
10
|
+
|
11
|
+
# @api private
|
12
|
+
attr_reader :admin
|
13
|
+
|
14
|
+
def initialize(admin)
|
15
|
+
@admin = admin
|
16
|
+
@client = admin.client
|
17
|
+
end
|
18
|
+
|
19
|
+
# (see Ably::Rest::Push::DeviceRegistrations#get)
|
20
|
+
#
|
21
|
+
# @yield Block is invoked when request succeeds
|
22
|
+
# @return [Ably::Util::SafeDeferrable]
|
23
|
+
#
|
24
|
+
def get(device_id, &callback)
|
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
|
+
async_wrap(callback) do
|
29
|
+
rest_device_registrations.get(device_id)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# (see Ably::Rest::Push::DeviceRegistrations#list)
|
34
|
+
#
|
35
|
+
# @yield Block is invoked when request succeeds
|
36
|
+
# @return [Ably::Util::SafeDeferrable]
|
37
|
+
#
|
38
|
+
def list(params = {}, &callback)
|
39
|
+
params = {} if params.nil?
|
40
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
41
|
+
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]
|
42
|
+
|
43
|
+
async_wrap(callback) do
|
44
|
+
rest_device_registrations.list(params.merge(async_blocking_operations: true))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# (see Ably::Rest::Push::DeviceRegistrations#save)
|
49
|
+
#
|
50
|
+
# @yield Block is invoked when request succeeds
|
51
|
+
# @return [Ably::Util::SafeDeferrable]
|
52
|
+
#
|
53
|
+
def save(device, &callback)
|
54
|
+
device_details = DeviceDetails(device)
|
55
|
+
raise ArgumentError, "Device ID is required yet is empty" if device_details.id.nil? || device_details == ''
|
56
|
+
|
57
|
+
async_wrap(callback) do
|
58
|
+
rest_device_registrations.save(device_details)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# (see Ably::Rest::Push::DeviceRegistrations#remove)
|
63
|
+
#
|
64
|
+
# @yield Block is invoked when request succeeds
|
65
|
+
# @return [Ably::Util::SafeDeferrable]
|
66
|
+
#
|
67
|
+
def remove(device_id, &callback)
|
68
|
+
device_id = device_id.id if device_id.kind_of?(Ably::Models::DeviceDetails)
|
69
|
+
raise ArgumentError, "device_id must be a string or DeviceDetails object" unless device_id.kind_of?(String)
|
70
|
+
|
71
|
+
async_wrap(callback) do
|
72
|
+
rest_device_registrations.remove(device_id)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# (see Ably::Rest::Push::DeviceRegistrations#remove_where)
|
77
|
+
#
|
78
|
+
# @yield Block is invoked when request succeeds
|
79
|
+
# @return [Ably::Util::SafeDeferrable]
|
80
|
+
#
|
81
|
+
def remove_where(params = {}, &callback)
|
82
|
+
filter = if params.kind_of?(Ably::Models::DeviceDetails)
|
83
|
+
{ 'deviceId' => params.id }
|
84
|
+
else
|
85
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
86
|
+
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]
|
87
|
+
IdiomaticRubyWrapper(params).as_json
|
88
|
+
end
|
89
|
+
|
90
|
+
async_wrap(callback) do
|
91
|
+
rest_device_registrations.remove_where(filter)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def rest_device_registrations
|
97
|
+
client.rest_client.push.admin.device_registrations
|
98
|
+
end
|
99
|
+
|
100
|
+
def logger
|
101
|
+
client.logger
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|