ably-rest 1.0.6 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -14
  3. data/lib/submodules/ably-ruby/.editorconfig +14 -0
  4. data/lib/submodules/ably-ruby/.travis.yml +4 -4
  5. data/lib/submodules/ably-ruby/CHANGELOG.md +43 -2
  6. data/lib/submodules/ably-ruby/README.md +3 -2
  7. data/lib/submodules/ably-ruby/Rakefile +32 -0
  8. data/lib/submodules/ably-ruby/SPEC.md +1277 -835
  9. data/lib/submodules/ably-ruby/ably.gemspec +9 -4
  10. data/lib/submodules/ably-ruby/lib/ably/auth.rb +30 -4
  11. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +8 -2
  12. data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +1 -1
  13. data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +1 -1
  14. data/lib/submodules/ably-ruby/lib/ably/models/device_details.rb +87 -0
  15. data/lib/submodules/ably-ruby/lib/ably/models/device_push_details.rb +86 -0
  16. data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +23 -2
  17. data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -4
  18. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +32 -2
  19. data/lib/submodules/ably-ruby/lib/ably/models/push_channel_subscription.rb +89 -0
  20. data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +1 -1
  21. data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +1 -1
  22. data/lib/submodules/ably-ruby/lib/ably/modules/exception_codes.rb +128 -0
  23. data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +15 -2
  24. data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +1 -1
  25. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +1 -0
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +1 -1
  27. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +24 -102
  28. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +2 -6
  29. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +2 -2
  30. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/publisher.rb +74 -0
  31. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/push_channel.rb +62 -0
  32. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +87 -0
  33. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +6 -2
  34. data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
  35. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +8 -5
  36. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +7 -7
  37. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +1 -1
  38. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +4 -4
  39. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +3 -3
  40. data/lib/submodules/ably-ruby/lib/ably/realtime/push.rb +40 -0
  41. data/lib/submodules/ably-ruby/lib/ably/realtime/push/admin.rb +61 -0
  42. data/lib/submodules/ably-ruby/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
  43. data/lib/submodules/ably-ruby/lib/ably/realtime/push/device_registrations.rb +105 -0
  44. data/lib/submodules/ably-ruby/lib/ably/rest.rb +1 -0
  45. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +33 -5
  46. data/lib/submodules/ably-ruby/lib/ably/rest/channel/push_channel.rb +62 -0
  47. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +138 -28
  48. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +17 -1
  49. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -0
  50. data/lib/submodules/ably-ruby/lib/ably/rest/push.rb +42 -0
  51. data/lib/submodules/ably-ruby/lib/ably/rest/push/admin.rb +54 -0
  52. data/lib/submodules/ably-ruby/lib/ably/rest/push/channel_subscriptions.rb +121 -0
  53. data/lib/submodules/ably-ruby/lib/ably/rest/push/device_registrations.rb +103 -0
  54. data/lib/submodules/ably-ruby/lib/ably/version.rb +7 -2
  55. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +233 -8
  56. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +166 -51
  57. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +149 -0
  58. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +1 -1
  59. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +4 -4
  60. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +19 -17
  61. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +5 -5
  62. data/lib/submodules/ably-ruby/spec/acceptance/realtime/push_admin_spec.rb +696 -0
  63. data/lib/submodules/ably-ruby/spec/acceptance/realtime/push_spec.rb +27 -0
  64. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +41 -3
  65. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +2 -2
  66. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +129 -10
  67. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +175 -4
  68. data/lib/submodules/ably-ruby/spec/acceptance/rest/push_admin_spec.rb +896 -0
  69. data/lib/submodules/ably-ruby/spec/acceptance/rest/push_spec.rb +25 -0
  70. data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +1 -1
  71. data/lib/submodules/ably-ruby/spec/run_parallel_tests +33 -0
  72. data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +10 -3
  73. data/lib/submodules/ably-ruby/spec/unit/models/device_details_spec.rb +102 -0
  74. data/lib/submodules/ably-ruby/spec/unit/models/device_push_details_spec.rb +101 -0
  75. data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +51 -3
  76. data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +17 -2
  77. data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +1 -1
  78. data/lib/submodules/ably-ruby/spec/unit/models/push_channel_subscription_spec.rb +86 -0
  79. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +12 -0
  80. data/lib/submodules/ably-ruby/spec/unit/realtime/push_channel_spec.rb +36 -0
  81. data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +8 -1
  82. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +30 -0
  83. data/lib/submodules/ably-ruby/spec/unit/rest/push_channel_spec.rb +36 -0
  84. metadata +29 -4
@@ -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, 80013)
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
- 91005
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, 91000)
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, 91001)
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, 91001)
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: 91004
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, 80013)
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, 80013)
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
@@ -1,6 +1,7 @@
1
1
  require 'ably/rest/channel'
2
2
  require 'ably/rest/channels'
3
3
  require 'ably/rest/client'
4
+ require 'ably/rest/push'
4
5
  require 'ably/rest/presence'
5
6
 
6
7
  require 'ably/models/message_encoders/base'
@@ -3,8 +3,6 @@ module Ably
3
3
  # The Ably Realtime service organises the traffic within any application into named channels.
4
4
  # Channels are the "unit" of message distribution; clients attach to channels to subscribe to messages, and every message broadcast by the service is associated with a unique channel.
5
5
  #
6
- # @!attribute [r] client
7
- # @return {Ably::Realtime::Client} Ably client associated with this channel
8
6
  # @!attribute [r] name
9
7
  # @return {String} channel name
10
8
  # @!attribute [r] options
@@ -12,7 +10,19 @@ module Ably
12
10
  class Channel
13
11
  include Ably::Modules::Conversions
14
12
 
15
- attr_reader :client, :name, :options
13
+ # Ably client associated with this channel
14
+ # @return [Ably::Realtime::Client]
15
+ # @api private
16
+ attr_reader :client
17
+
18
+ attr_reader :name, :options
19
+
20
+ # Push channel used for push notification (client-side)
21
+ # @return [Ably::Rest::Channel::PushChannel]
22
+ # @api private
23
+ attr_reader :push
24
+
25
+ IDEMPOTENT_LIBRARY_GENERATED_ID_LENGTH = 9 # See spec RSL1k1
16
26
 
17
27
  # Initialize a new Channel object
18
28
  #
@@ -27,13 +37,14 @@ module Ably
27
37
  update_options channel_options
28
38
  @client = client
29
39
  @name = name
40
+ @push = PushChannel.new(self)
30
41
  end
31
42
 
32
43
  # Publish one or more messages to the channel.
33
44
  #
34
45
  # @param name [String, Array<Ably::Models::Message|Hash>, nil] The event name of the message to publish, or an Array of [Ably::Model::Message] objects or [Hash] objects with +:name+ and +:data+ pairs
35
46
  # @param data [String, ByteArray, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument
36
- # @param attributes [Hash, nil] Optional additional message attributes such as :client_id or :connection_id, applied when name attribute is nil or a string
47
+ # @param attributes [Hash, nil] Optional additional message attributes such as :extras, :id, :client_id or :connection_id, applied when name attribute is nil or a string
37
48
  # @return [Boolean] true if the message was published, otherwise false
38
49
  #
39
50
  # @example
@@ -58,12 +69,16 @@ module Ably
58
69
  messages = if name.kind_of?(Enumerable)
59
70
  name
60
71
  else
72
+ if name.kind_of?(Ably::Models::Message)
73
+ raise ArgumentError, "name argument does not support single Message objects, only arrays of Message objects"
74
+ end
75
+
61
76
  name = ensure_utf_8(:name, name, allow_nil: true)
62
77
  ensure_supported_payload data
63
78
  [{ name: name, data: data }.merge(attributes)]
64
79
  end
65
80
 
66
- payload = messages.map do |message|
81
+ payload = messages.each_with_index.map do |message, index|
67
82
  Ably::Models::Message(message.dup).tap do |msg|
68
83
  msg.encode client.encoders, options
69
84
 
@@ -75,6 +90,17 @@ module Ably
75
90
  raise Ably::Exceptions::IncompatibleClientId.new("Cannot publish with client_id '#{msg.client_id}' as it is incompatible with the current configured client_id '#{client.client_id}'")
76
91
  end
77
92
  end.as_json
93
+ end.tap do |payload|
94
+ if client.idempotent_rest_publishing
95
+ # We cannot mutate for idempotent publishing if one or more messages already has an ID
96
+ if payload.all? { |msg| !msg['id'] }
97
+ # Mutate the JSON to support idempotent publishing where a Message.id does not exist
98
+ idempotent_publish_id = SecureRandom.base64(IDEMPOTENT_LIBRARY_GENERATED_ID_LENGTH)
99
+ payload.each_with_index do |msg, idx|
100
+ msg['id'] = "#{idempotent_publish_id}:#{idx}"
101
+ end
102
+ end
103
+ end
78
104
  end
79
105
 
80
106
  response = client.post("#{base_path}/publish", payload.length == 1 ? payload.first : payload)
@@ -141,3 +167,5 @@ module Ably
141
167
  end
142
168
  end
143
169
  end
170
+
171
+ require 'ably/rest/channel/push_channel'