ably 1.0.7 → 1.1.0
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/.editorconfig +14 -0
- data/.travis.yml +4 -4
- data/CHANGELOG.md +26 -3
- data/Rakefile +32 -0
- data/SPEC.md +920 -565
- data/ably.gemspec +9 -4
- data/lib/ably/auth.rb +28 -2
- data/lib/ably/exceptions.rb +8 -2
- data/lib/ably/models/channel_state_change.rb +1 -1
- data/lib/ably/models/connection_state_change.rb +1 -1
- data/lib/ably/models/device_details.rb +87 -0
- data/lib/ably/models/device_push_details.rb +86 -0
- data/lib/ably/models/error_info.rb +23 -2
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -4
- data/lib/ably/models/protocol_message.rb +32 -2
- data/lib/ably/models/push_channel_subscription.rb +89 -0
- data/lib/ably/modules/conversions.rb +1 -1
- data/lib/ably/modules/encodeable.rb +1 -1
- data/lib/ably/modules/exception_codes.rb +128 -0
- data/lib/ably/modules/model_common.rb +15 -2
- data/lib/ably/modules/state_machine.rb +1 -1
- data/lib/ably/realtime.rb +1 -0
- data/lib/ably/realtime/auth.rb +1 -1
- data/lib/ably/realtime/channel.rb +24 -102
- data/lib/ably/realtime/channel/channel_manager.rb +2 -6
- data/lib/ably/realtime/channel/channel_state_machine.rb +2 -2
- data/lib/ably/realtime/channel/publisher.rb +74 -0
- data/lib/ably/realtime/channel/push_channel.rb +62 -0
- data/lib/ably/realtime/client.rb +87 -0
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +6 -2
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
- data/lib/ably/realtime/connection.rb +8 -5
- data/lib/ably/realtime/connection/connection_manager.rb +7 -7
- data/lib/ably/realtime/connection/websocket_transport.rb +1 -1
- data/lib/ably/realtime/presence.rb +4 -4
- data/lib/ably/realtime/presence/members_map.rb +3 -3
- data/lib/ably/realtime/push.rb +40 -0
- data/lib/ably/realtime/push/admin.rb +61 -0
- data/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
- data/lib/ably/realtime/push/device_registrations.rb +105 -0
- data/lib/ably/rest.rb +1 -0
- data/lib/ably/rest/channel.rb +33 -5
- data/lib/ably/rest/channel/push_channel.rb +62 -0
- data/lib/ably/rest/client.rb +137 -28
- data/lib/ably/rest/middleware/parse_message_pack.rb +17 -1
- data/lib/ably/rest/presence.rb +1 -0
- data/lib/ably/rest/push.rb +42 -0
- data/lib/ably/rest/push/admin.rb +54 -0
- data/lib/ably/rest/push/channel_subscriptions.rb +121 -0
- data/lib/ably/rest/push/device_registrations.rb +103 -0
- data/lib/ably/version.rb +7 -2
- data/spec/acceptance/realtime/auth_spec.rb +6 -8
- data/spec/acceptance/realtime/channel_spec.rb +166 -51
- data/spec/acceptance/realtime/client_spec.rb +149 -0
- data/spec/acceptance/realtime/connection_failures_spec.rb +1 -1
- data/spec/acceptance/realtime/connection_spec.rb +4 -4
- data/spec/acceptance/realtime/message_spec.rb +19 -17
- data/spec/acceptance/realtime/presence_spec.rb +5 -5
- data/spec/acceptance/realtime/push_admin_spec.rb +696 -0
- data/spec/acceptance/realtime/push_spec.rb +27 -0
- data/spec/acceptance/rest/auth_spec.rb +4 -3
- data/spec/acceptance/rest/base_spec.rb +2 -2
- data/spec/acceptance/rest/client_spec.rb +129 -10
- data/spec/acceptance/rest/message_spec.rb +175 -4
- data/spec/acceptance/rest/push_admin_spec.rb +896 -0
- data/spec/acceptance/rest/push_spec.rb +25 -0
- data/spec/acceptance/rest/time_spec.rb +1 -1
- data/spec/run_parallel_tests +33 -0
- data/spec/unit/logger_spec.rb +10 -3
- data/spec/unit/models/device_details_spec.rb +102 -0
- data/spec/unit/models/device_push_details_spec.rb +101 -0
- data/spec/unit/models/error_info_spec.rb +51 -3
- data/spec/unit/models/message_spec.rb +17 -2
- data/spec/unit/models/presence_message_spec.rb +1 -1
- data/spec/unit/models/push_channel_subscription_spec.rb +86 -0
- data/spec/unit/realtime/client_spec.rb +12 -0
- data/spec/unit/realtime/push_channel_spec.rb +36 -0
- data/spec/unit/rest/channel_spec.rb +8 -1
- data/spec/unit/rest/client_spec.rb +30 -0
- data/spec/unit/rest/push_channel_spec.rb +36 -0
- metadata +71 -8
@@ -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
|
data/lib/ably/version.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Ably
|
2
|
-
VERSION = '1.0
|
3
|
-
PROTOCOL_VERSION = '1.
|
2
|
+
VERSION = '1.1.0'
|
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
|
@@ -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: lambda { |token_params|
|
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
|
@@ -749,11 +750,8 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
749
750
|
end
|
750
751
|
|
751
752
|
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 }) }
|
753
|
+
# Ably will send AUTH 30 seconds before expiry
|
754
|
+
let(:client_options) { default_options.merge(use_token_auth: :true, default_token_params: { ttl: 33 }) }
|
757
755
|
|
758
756
|
it 'should immediately start a new authentication process (#RTN22)' do
|
759
757
|
client.connection.once(:connected) do
|
@@ -13,6 +13,9 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
13
13
|
let(:channel) { client.channel(channel_name) }
|
14
14
|
let(:messages) { [] }
|
15
15
|
|
16
|
+
let(:sub_client) { auto_close Ably::Realtime::Client.new(client_options) }
|
17
|
+
let(:sub_channel) { sub_client.channel(channel_name) }
|
18
|
+
|
16
19
|
def disconnect_transport
|
17
20
|
connection.transport.unbind
|
18
21
|
end
|
@@ -726,7 +729,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
726
729
|
let(:name) { random_str }
|
727
730
|
let(:data) { random_str }
|
728
731
|
|
729
|
-
context 'when attached' do
|
732
|
+
context 'when channel is attached (#RTL6c1)' do
|
730
733
|
it 'publishes messages' do
|
731
734
|
channel.attach do
|
732
735
|
3.times { channel.publish('event', payload) }
|
@@ -738,81 +741,173 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
738
741
|
end
|
739
742
|
end
|
740
743
|
|
741
|
-
context 'when not
|
742
|
-
it 'publishes
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
744
|
+
context 'when channel is not attached in state Initializing (#RTL6c1)' do
|
745
|
+
it 'publishes messages immediately and does not implicitly attach (#RTL6c1)' do
|
746
|
+
sub_channel.attach do
|
747
|
+
sub_channel.subscribe do |message|
|
748
|
+
messages << message if message.name == 'event'
|
749
|
+
if messages.count == 3
|
750
|
+
EventMachine.add_timer(1) do
|
751
|
+
expect(channel.state).to eq(:initialized)
|
752
|
+
stop_reactor
|
753
|
+
end
|
754
|
+
end
|
755
|
+
end
|
756
|
+
3.times { channel.publish('event', random_str) }
|
747
757
|
end
|
748
758
|
end
|
759
|
+
end
|
749
760
|
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
761
|
+
context 'when channel is Attaching (#RTL6c1)' do
|
762
|
+
it 'publishes messages immediately (#RTL6c1)' do
|
763
|
+
sub_channel.attach do
|
764
|
+
channel.once(:attaching) do
|
765
|
+
outgoing_message_count = 0
|
766
|
+
client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
|
767
|
+
if protocol_message.action == :message
|
768
|
+
raise "Expected channel state to be attaching when publishing messages, not #{channel.state}" unless channel.attaching?
|
769
|
+
outgoing_message_count += protocol_message.messages.count
|
770
|
+
end
|
771
|
+
end
|
772
|
+
sub_channel.subscribe do |message|
|
773
|
+
messages << message if message.name == 'event'
|
774
|
+
if messages.count == 3
|
775
|
+
expect(outgoing_message_count).to eql(3)
|
776
|
+
stop_reactor
|
777
|
+
end
|
778
|
+
end
|
779
|
+
3.times { channel.publish('event', random_str) }
|
780
|
+
end
|
781
|
+
channel.attach
|
766
782
|
end
|
767
783
|
end
|
784
|
+
end
|
768
785
|
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
786
|
+
context 'when channel is Detaching (#RTL6c1)' do
|
787
|
+
it 'publishes messages immediately (#RTL6c1)' do
|
788
|
+
sub_channel.attach do
|
789
|
+
channel.attach do
|
790
|
+
channel.once(:detaching) do
|
791
|
+
outgoing_message_count = 0
|
792
|
+
client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
|
793
|
+
if protocol_message.action == :message
|
794
|
+
raise "Expected channel state to be attaching when publishing messages, not #{channel.state}" unless channel.detaching?
|
795
|
+
outgoing_message_count += protocol_message.messages.count
|
796
|
+
end
|
797
|
+
end
|
798
|
+
sub_channel.subscribe do |message|
|
799
|
+
messages << message if message.name == 'event'
|
800
|
+
if messages.count == 3
|
801
|
+
expect(outgoing_message_count).to eql(3)
|
802
|
+
stop_reactor
|
803
|
+
end
|
804
|
+
end
|
805
|
+
3.times { channel.publish('event', random_str) }
|
778
806
|
end
|
807
|
+
channel.detach
|
779
808
|
end
|
780
809
|
end
|
810
|
+
end
|
811
|
+
end
|
781
812
|
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
813
|
+
context 'when channel is Detached (#RTL6c1)' do
|
814
|
+
it 'publishes messages immediately (#RTL6c1)' do
|
815
|
+
sub_channel.attach do
|
816
|
+
channel.attach
|
817
|
+
channel.once(:attached) do
|
818
|
+
channel.once(:detached) do
|
819
|
+
outgoing_message_count = 0
|
820
|
+
client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
|
821
|
+
if protocol_message.action == :message
|
822
|
+
raise "Expected channel state to be attaching when publishing messages, not #{channel.state}" unless channel.detached?
|
823
|
+
outgoing_message_count += protocol_message.messages.count
|
824
|
+
end
|
825
|
+
end
|
826
|
+
sub_channel.subscribe do |message|
|
827
|
+
messages << message if message.name == 'event'
|
828
|
+
if messages.count == 3
|
829
|
+
expect(outgoing_message_count).to eql(3)
|
830
|
+
stop_reactor
|
831
|
+
end
|
790
832
|
end
|
833
|
+
3.times { channel.publish('event', random_str) }
|
834
|
+
end
|
835
|
+
channel.detach
|
836
|
+
end
|
837
|
+
end
|
838
|
+
end
|
839
|
+
end
|
840
|
+
|
841
|
+
context 'with :queue_messages client option set to false (#RTL6c4)' do
|
842
|
+
let(:client_options) { default_options.merge(queue_messages: false) }
|
843
|
+
|
844
|
+
context 'and connection state connected (#RTL6c4)' do
|
845
|
+
it 'publishes the message' do
|
846
|
+
client.connection.once(:connected) do
|
847
|
+
channel.publish('event')
|
848
|
+
stop_reactor
|
849
|
+
end
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
context 'and connection state initialized (#RTL6c4)' do
|
854
|
+
it 'fails the deferrable' do
|
855
|
+
expect(client.connection).to be_initialized
|
856
|
+
channel.publish('event').errback do |error|
|
857
|
+
expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
|
858
|
+
stop_reactor
|
859
|
+
end
|
860
|
+
end
|
861
|
+
end
|
862
|
+
|
863
|
+
context 'and connection state connecting (#RTL6c4)' do
|
864
|
+
it 'fails the deferrable' do
|
865
|
+
client.connect
|
866
|
+
EventMachine.next_tick do
|
867
|
+
expect(client.connection).to be_connecting
|
868
|
+
channel.publish('event').errback do |error|
|
869
|
+
expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
|
870
|
+
stop_reactor
|
791
871
|
end
|
792
872
|
end
|
793
873
|
end
|
874
|
+
end
|
794
875
|
|
795
|
-
|
876
|
+
[:disconnected, :suspended, :closing, :closed].each do |invalid_connection_state|
|
877
|
+
context "and connection state #{invalid_connection_state} (#RTL6c4)" do
|
796
878
|
let(:client_options) { default_options.merge(queue_messages: false) }
|
797
879
|
it 'fails the deferrable' do
|
798
880
|
client.connection.once(:connected) do
|
799
|
-
client.connection.once(
|
800
|
-
expect(client.connection).to
|
881
|
+
client.connection.once(invalid_connection_state) do
|
882
|
+
expect(client.connection.state).to eq(invalid_connection_state)
|
801
883
|
channel.publish('event').errback do |error|
|
802
884
|
expect(error).to be_a(Ably::Exceptions::MessageQueueingDisabled)
|
803
885
|
stop_reactor
|
804
886
|
end
|
805
887
|
end
|
806
|
-
|
888
|
+
if invalid_connection_state == :closed
|
889
|
+
connection.close
|
890
|
+
else
|
891
|
+
client.connection.transition_state_machine invalid_connection_state
|
892
|
+
end
|
807
893
|
end
|
808
894
|
end
|
809
895
|
end
|
896
|
+
end
|
810
897
|
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
898
|
+
context 'and the channel state is failed (#RTL6c4)' do
|
899
|
+
let(:client_options) { default_options.merge(queue_messages: false) }
|
900
|
+
it 'fails the deferrable' do
|
901
|
+
client.connection.once(:connected) do
|
902
|
+
channel.attach
|
903
|
+
channel.once(:attached) do
|
904
|
+
channel.once(:failed) do
|
905
|
+
channel.publish('event').errback do |error|
|
906
|
+
expect(error).to be_a(Ably::Exceptions::ChannelInactive)
|
907
|
+
stop_reactor
|
908
|
+
end
|
909
|
+
end
|
910
|
+
channel.transition_state_machine(:failed)
|
816
911
|
end
|
817
912
|
end
|
818
913
|
end
|
@@ -1074,6 +1169,21 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1074
1169
|
end
|
1075
1170
|
end
|
1076
1171
|
|
1172
|
+
context 'with more than allowed messages in a single publish' do
|
1173
|
+
let(:channel_name) { random_str }
|
1174
|
+
|
1175
|
+
it 'rejects the publish' do
|
1176
|
+
messages = (Ably::Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE + 1).times.map do
|
1177
|
+
{ name: 'foo' }
|
1178
|
+
end
|
1179
|
+
|
1180
|
+
channel.publish(messages).errback do |error|
|
1181
|
+
expect(error).to be_kind_of(Ably::Exceptions::InvalidRequest)
|
1182
|
+
stop_reactor
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
end
|
1186
|
+
|
1077
1187
|
context 'identified clients' do
|
1078
1188
|
context 'when authenticated with a wildcard client_id' do
|
1079
1189
|
let(:token) { Ably::Rest::Client.new(default_options).auth.request_token(client_id: '*') }
|
@@ -1953,8 +2063,12 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1953
2063
|
end
|
1954
2064
|
|
1955
2065
|
context 'moves to' do
|
1956
|
-
%w(suspended
|
2066
|
+
%w(suspended failed).each do |channel_state|
|
1957
2067
|
context(channel_state) do
|
2068
|
+
let(:client) do
|
2069
|
+
auto_close Ably::Realtime::Client.new(default_options.merge(log_level: :error))
|
2070
|
+
end
|
2071
|
+
|
1958
2072
|
specify 'all queued messages fail with NACK (#RTL11)' do
|
1959
2073
|
channel.attach do
|
1960
2074
|
# Move to disconnected
|
@@ -1981,7 +2095,8 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1981
2095
|
specify 'all published messages awaiting an ACK do nothing (#RTL11a)' do
|
1982
2096
|
connection_been_disconnected = false
|
1983
2097
|
|
1984
|
-
channel.attach
|
2098
|
+
channel.attach
|
2099
|
+
channel.once(:attached) do
|
1985
2100
|
deferrable = channel.publish("foo")
|
1986
2101
|
deferrable.errback do |error|
|
1987
2102
|
fail "Message publish should not fail"
|
@@ -12,6 +12,7 @@ describe Ably::Realtime::Client, :event_machine do
|
|
12
12
|
let(:auth_params) { subject.auth.auth_params_sync }
|
13
13
|
|
14
14
|
subject { auto_close Ably::Realtime::Client.new(client_options) }
|
15
|
+
let(:sub_client) { auto_close Ably::Realtime::Client.new(client_options) }
|
15
16
|
|
16
17
|
context 'initialization' do
|
17
18
|
context 'basic auth' do
|
@@ -24,6 +25,22 @@ describe Ably::Realtime::Client, :event_machine do
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
28
|
+
context 'with an invalid API key' do
|
29
|
+
let(:custom_logger_object) { TestLogger.new }
|
30
|
+
let(:client) { Ably::Realtime::Client.new(client_options.merge(key: 'app.key:secret', logger: custom_logger_object)) }
|
31
|
+
|
32
|
+
it 'logs an entry with a help href url matching the code #TI5' do
|
33
|
+
client.connect
|
34
|
+
client.connection.once(:failed) do
|
35
|
+
expect(custom_logger_object.logs.find do |severity, message|
|
36
|
+
next unless %w(fatal error).include?(severity.to_s)
|
37
|
+
message.match(%r{https://help.ably.io/error/40400})
|
38
|
+
end).to_not be_nil
|
39
|
+
stop_reactor
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
27
44
|
context ':tls option' do
|
28
45
|
context 'set to false to force a plain-text connection' do
|
29
46
|
let(:client_options) { default_options.merge(tls: false, log_level: :none) }
|
@@ -281,5 +298,137 @@ describe Ably::Realtime::Client, :event_machine do
|
|
281
298
|
end
|
282
299
|
end
|
283
300
|
end
|
301
|
+
|
302
|
+
context '#publish (#TBC)' do
|
303
|
+
let(:channel_name) { random_str }
|
304
|
+
let(:channel) { subject.channel(channel_name) }
|
305
|
+
let(:sub_channel) { sub_client.channel(channel_name) }
|
306
|
+
let(:event_name) { random_str }
|
307
|
+
let(:data) { random_str }
|
308
|
+
let(:extras) { { 'push' => { 'notification' => { 'title' => 'Testing' } } } }
|
309
|
+
let(:message) { Ably::Models::Message.new(name: event_name, data: data) }
|
310
|
+
|
311
|
+
specify 'publishing a message implicity connects and publishes the message successfully on the provided channel' do
|
312
|
+
sub_channel.attach do
|
313
|
+
sub_channel.subscribe do |msg|
|
314
|
+
expect(msg.name).to eql(event_name)
|
315
|
+
expect(msg.data).to eql(data)
|
316
|
+
stop_reactor
|
317
|
+
end
|
318
|
+
end
|
319
|
+
subject.publish channel_name, event_name, data
|
320
|
+
end
|
321
|
+
|
322
|
+
specify 'publishing does not result in a channel being created' do
|
323
|
+
subject.publish channel_name, event_name, data
|
324
|
+
subject.channels.fetch(channel_name) do
|
325
|
+
# Block called if channel does not exist
|
326
|
+
EventMachine.add_timer(1) do
|
327
|
+
subject.channels.fetch(channel_name) do
|
328
|
+
# Block called if channel does not exist
|
329
|
+
stop_reactor
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
context 'with extras' do
|
336
|
+
let(:channel_name) { "pushenabled:#{random_str}" }
|
337
|
+
|
338
|
+
specify 'publishing supports extras' do
|
339
|
+
sub_channel.attach do
|
340
|
+
sub_channel.subscribe do |msg|
|
341
|
+
expect(msg.extras).to eql(extras)
|
342
|
+
stop_reactor
|
343
|
+
end
|
344
|
+
end
|
345
|
+
subject.publish channel_name, event_name, {}, extras: extras
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
specify 'publishing supports an array of Message objects' do
|
350
|
+
sub_channel.attach do
|
351
|
+
sub_channel.subscribe do |msg|
|
352
|
+
expect(msg.name).to eql(event_name)
|
353
|
+
expect(msg.data).to eql(data)
|
354
|
+
stop_reactor
|
355
|
+
end
|
356
|
+
end
|
357
|
+
subject.publish channel_name, [message]
|
358
|
+
end
|
359
|
+
|
360
|
+
specify 'publishing supports an array of Hash objects' do
|
361
|
+
sub_channel.attach do
|
362
|
+
sub_channel.subscribe do |msg|
|
363
|
+
expect(msg.name).to eql(event_name)
|
364
|
+
expect(msg.data).to eql(data)
|
365
|
+
stop_reactor
|
366
|
+
end
|
367
|
+
end
|
368
|
+
subject.publish channel_name, [name: event_name, data: data]
|
369
|
+
end
|
370
|
+
|
371
|
+
specify 'publishing on a closed connection fails' do
|
372
|
+
subject.connection.once(:connected) do
|
373
|
+
subject.connection.once(:closed) do
|
374
|
+
subject.publish(channel_name, name: event_name).errback do |error|
|
375
|
+
expect(error).to be_kind_of(Ably::Exceptions::MessageQueueingDisabled)
|
376
|
+
stop_reactor
|
377
|
+
end
|
378
|
+
end
|
379
|
+
connection.close
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
context 'queue_messages ClientOption' do
|
384
|
+
context 'when true' do
|
385
|
+
subject { auto_close Ably::Realtime::Client.new(client_options.merge(auto_connect: false)) }
|
386
|
+
|
387
|
+
it 'will queue messages whilst connecting and publish once connected' do
|
388
|
+
sub_channel.attach do
|
389
|
+
sub_channel.subscribe do |msg|
|
390
|
+
expect(msg.name).to eql(event_name)
|
391
|
+
stop_reactor
|
392
|
+
end
|
393
|
+
subject.connection.once(:connecting) do
|
394
|
+
subject.publish channel_name, event_name
|
395
|
+
end
|
396
|
+
subject.connection.connect
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
context 'when false' do
|
402
|
+
subject { auto_close Ably::Realtime::Client.new(client_options.merge(auto_connect: false, queue_messages: false)) }
|
403
|
+
|
404
|
+
it 'will reject messages on an initializing connection' do
|
405
|
+
sub_channel.attach do
|
406
|
+
subject.connection.once(:connecting) do
|
407
|
+
subject.publish(channel_name, event_name).errback do |error|
|
408
|
+
expect(error).to be_kind_of(Ably::Exceptions::MessageQueueingDisabled)
|
409
|
+
stop_reactor
|
410
|
+
end
|
411
|
+
end
|
412
|
+
subject.connection.connect
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
context 'with more than allowed messages in a single publish' do
|
419
|
+
let(:channel_name) { random_str }
|
420
|
+
|
421
|
+
it 'rejects the publish' do
|
422
|
+
messages = (Ably::Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE + 1).times.map do
|
423
|
+
{ name: 'foo' }
|
424
|
+
end
|
425
|
+
|
426
|
+
subject.publish(channel_name, messages).errback do |error|
|
427
|
+
expect(error).to be_kind_of(Ably::Exceptions::InvalidRequest)
|
428
|
+
stop_reactor
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
284
433
|
end
|
285
434
|
end
|