ably-rest 0.7.5 → 0.8.1

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.
Files changed (76) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +2 -0
  3. data/README.md +41 -15
  4. data/SPEC.md +654 -518
  5. data/lib/submodules/ably-ruby/.gitignore +1 -0
  6. data/lib/submodules/ably-ruby/.gitmodules +3 -0
  7. data/lib/submodules/ably-ruby/README.md +54 -26
  8. data/lib/submodules/ably-ruby/SPEC.md +468 -322
  9. data/lib/submodules/ably-ruby/ably.gemspec +4 -2
  10. data/lib/submodules/ably-ruby/lib/ably/auth.rb +185 -131
  11. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +1 -1
  12. data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +31 -44
  13. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +2 -2
  14. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +1 -2
  15. data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +67 -24
  16. data/lib/submodules/ably-ruby/lib/ably/models/stats_types.rb +131 -0
  17. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +101 -0
  18. data/lib/submodules/ably-ruby/lib/ably/models/token_request.rb +108 -0
  19. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +3 -2
  20. data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +1 -1
  21. data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +2 -2
  22. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +3 -7
  23. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +32 -5
  24. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +1 -0
  25. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +4 -8
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +5 -3
  27. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +12 -1
  28. data/lib/submodules/ably-ruby/lib/ably/rest.rb +3 -7
  29. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +13 -10
  30. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +19 -20
  31. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +14 -12
  32. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  33. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +74 -23
  34. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +3 -3
  35. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +18 -18
  36. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +5 -5
  37. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +12 -12
  38. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +5 -5
  39. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +56 -13
  40. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +8 -8
  41. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +1 -1
  42. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +1 -1
  43. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +262 -158
  44. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +11 -9
  45. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +28 -21
  46. data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +1 -1
  47. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +30 -27
  48. data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +1 -1
  49. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +10 -10
  50. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +93 -56
  51. data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +50 -45
  52. data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +1 -1
  53. data/lib/submodules/ably-ruby/spec/rspec_config.rb +3 -2
  54. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +36 -28
  55. data/lib/submodules/ably-ruby/spec/spec_helper.rb +3 -0
  56. data/lib/submodules/ably-ruby/spec/support/api_helper.rb +3 -3
  57. data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +1 -1
  58. data/lib/submodules/ably-ruby/spec/support/test_app.rb +20 -33
  59. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +18 -1
  60. data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +81 -72
  61. data/lib/submodules/ably-ruby/spec/unit/models/stats_spec.rb +289 -0
  62. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +111 -0
  63. data/lib/submodules/ably-ruby/spec/unit/models/token_request_spec.rb +110 -0
  64. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +1 -1
  65. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +1 -1
  66. data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +1 -1
  67. data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +1 -1
  68. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +8 -8
  69. data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +1 -1
  70. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +1 -1
  71. metadata +9 -7
  72. data/lib/submodules/ably-ruby/lib/ably/models/token.rb +0 -74
  73. data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +0 -56
  74. data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +0 -56
  75. data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +0 -113
  76. data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +0 -86
@@ -6,7 +6,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
6
6
 
7
7
  vary_by_protocol do
8
8
  let(:default_options) do
9
- { api_key: api_key, environment: environment, protocol: protocol }
9
+ { key: api_key, environment: environment, protocol: protocol }
10
10
  end
11
11
 
12
12
  let(:client_options) { default_options }
@@ -16,12 +16,12 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
16
16
 
17
17
  context 'authentication failure' do
18
18
  let(:client_options) do
19
- default_options.merge(api_key: invalid_key, log_level: :none)
19
+ default_options.merge(key: invalid_key, log_level: :none)
20
20
  end
21
21
 
22
22
  context 'when API key is invalid' do
23
23
  context 'with invalid app part of the key' do
24
- let(:invalid_key) { 'not_an_app.invalid_key_id:invalid_key_value' }
24
+ let(:invalid_key) { 'not_an_app.invalid_key_name:invalid_key_value' }
25
25
 
26
26
  it 'enters the failed state and returns a not found error' do
27
27
  connection.on(:failed) do |error|
@@ -34,8 +34,8 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
34
34
  end
35
35
  end
36
36
 
37
- context 'with invalid key ID part of the key' do
38
- let(:invalid_key) { "#{app_id}.invalid_key_id:invalid_key_value" }
37
+ context 'with invalid key name part of the key' do
38
+ let(:invalid_key) { "#{app_id}.invalid_key_name:invalid_key_value" }
39
39
 
40
40
  it 'enters the failed state and returns an authorization error' do
41
41
  connection.on(:failed) do |error|
@@ -7,7 +7,7 @@ describe Ably::Realtime::Connection, :event_machine do
7
7
 
8
8
  vary_by_protocol do
9
9
  let(:default_options) do
10
- { api_key: api_key, environment: environment, protocol: protocol }
10
+ { key: api_key, environment: environment, protocol: protocol }
11
11
  end
12
12
 
13
13
  let(:client_options) { default_options }
@@ -52,7 +52,7 @@ describe Ably::Realtime::Connection, :event_machine do
52
52
  before do
53
53
  # Reduce token expiry buffer to zero so that a token expired? predicate is exact
54
54
  # Normally there is a buffer so that a token expiring soon is considered expired
55
- stub_const 'Ably::Models::Token::TOKEN_EXPIRY_BUFFER', 0
55
+ stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', 0
56
56
  end
57
57
 
58
58
  context 'for renewable tokens' do
@@ -93,17 +93,17 @@ describe Ably::Realtime::Connection, :event_machine do
93
93
 
94
94
  it 'renews the token on connect' do
95
95
  sleep ttl + 0.1
96
- expect(client.auth.current_token).to be_expired
96
+ expect(client.auth.current_token_details).to be_expired
97
97
  expect(client.auth).to receive(:authorise).at_least(:once).and_call_original
98
98
  connection.once(:connected) do
99
- expect(client.auth.current_token).to_not be_expired
99
+ expect(client.auth.current_token_details).to_not be_expired
100
100
  stop_reactor
101
101
  end
102
102
  end
103
103
  end
104
104
 
105
105
  context 'with immediately expiring token' do
106
- let(:ttl) { 0.01 }
106
+ let(:ttl) { 0.001 }
107
107
 
108
108
  it 'renews the token on connect, and only makes one subsequent attempt to obtain a new token' do
109
109
  expect(client.auth).to receive(:authorise).at_least(:twice).and_call_original
@@ -138,19 +138,19 @@ describe Ably::Realtime::Connection, :event_machine do
138
138
 
139
139
  context 'when connected with a valid non-expired token' do
140
140
  context 'that then expires following the connection being opened' do
141
- let(:ttl) { 2 }
141
+ let(:ttl) { 5 }
142
142
  let(:channel) { client.channel('test') }
143
143
 
144
144
  context 'the server' do
145
- it 'disconnects the client, and the client automatically renews the token and then reconnects', em_timeout: 10 do
146
- original_token = client.auth.current_token
145
+ it 'disconnects the client, and the client automatically renews the token and then reconnects', em_timeout: 15 do
146
+ original_token = client.auth.current_token_details
147
147
  expect(original_token).to_not be_expired
148
148
 
149
149
  connection.once(:connected) do
150
150
  started_at = Time.now
151
151
  connection.once(:disconnected) do |error|
152
152
  connection.once(:connected) do
153
- expect(client.auth.current_token).to_not be_expired
153
+ expect(client.auth.current_token_details).to_not be_expired
154
154
  expect(Time.now - started_at >= ttl)
155
155
  expect(original_token).to be_expired
156
156
  expect(error.code).to eql(40140) # token expired
@@ -172,16 +172,16 @@ describe Ably::Realtime::Connection, :event_machine do
172
172
 
173
173
  context 'for non-renewable tokens' do
174
174
  context 'that are expired' do
175
- let!(:expired_token) do
175
+ let!(:expired_token_details) do
176
176
  Ably::Realtime::Client.new(default_options).auth.request_token(ttl: 0.01)
177
177
  end
178
178
 
179
179
  context 'opening a new connection' do
180
- let(:client_options) { default_options.merge(api_key: nil, token_id: expired_token.id, log_level: :none) }
180
+ let(:client_options) { default_options.merge(key: nil, token: expired_token_details.token, log_level: :none) }
181
181
 
182
182
  it 'transitions state to failed', em_timeout: 10 do
183
183
  EventMachine.add_timer(1) do # wait for token to expire
184
- expect(expired_token).to be_expired
184
+ expect(expired_token_details).to be_expired
185
185
  connection.once(:connected) { raise 'Connection should never connect as token has expired' }
186
186
  connection.once(:failed) do
187
187
  expect(client.connection.error_reason.code).to eql(40140)
@@ -6,7 +6,7 @@ require 'securerandom'
6
6
 
7
7
  describe 'Ably::Realtime::Channel Message', :event_machine do
8
8
  vary_by_protocol do
9
- let(:default_options) { options.merge(api_key: api_key, environment: environment, protocol: protocol) }
9
+ let(:default_options) { options.merge(key: api_key, environment: environment, protocol: protocol) }
10
10
  let(:client_options) { default_options }
11
11
  let(:client) do
12
12
  Ably::Realtime::Client.new(client_options)
@@ -77,8 +77,8 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
77
77
  context 'when retrieved over REST' do
78
78
  it 'matches the sender connection#id' do
79
79
  channel.publish('event', payload) do
80
- channel.history do |messages|
81
- expect(messages.first.connection_id).to eql(client.connection.id)
80
+ channel.history do |page|
81
+ expect(page.items.first.connection_id).to eql(client.connection.id)
82
82
  stop_reactor
83
83
  end
84
84
  end
@@ -175,7 +175,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
175
175
 
176
176
  context 'without suitable publishing permissions' do
177
177
  let(:restricted_client) do
178
- Ably::Realtime::Client.new(options.merge(api_key: restricted_api_key, environment: environment, protocol: protocol))
178
+ Ably::Realtime::Client.new(options.merge(key: restricted_api_key, environment: environment, protocol: protocol))
179
179
  end
180
180
  let(:restricted_channel) { restricted_client.channel("cansubscribe:example") }
181
181
  let(:payload) { 'Test message without permission to publish' }
@@ -293,7 +293,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
293
293
  end
294
294
  end
295
295
 
296
- resources_root = File.expand_path('../../../resources', __FILE__)
296
+ resources_root = File.expand_path('../../../../lib/submodules/ably-common/test-resources', __FILE__)
297
297
 
298
298
  def self.add_tests_for_data(data)
299
299
  data['items'].each_with_index do |item, index|
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
 
4
4
  describe Ably::Realtime::Presence, 'history', :event_machine do
5
5
  vary_by_protocol do
6
- let(:default_options) { { api_key: api_key, environment: environment, protocol: protocol } }
6
+ let(:default_options) { { key: api_key, environment: environment, protocol: protocol } }
7
7
 
8
8
  let(:channel_name) { "persisted:#{random_str(2)}" }
9
9
 
@@ -21,16 +21,16 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
21
21
  it 'provides up to the moment presence history' do
22
22
  presence_client_one.enter(data: data) do
23
23
  presence_client_one.leave(data: leave_data) do
24
- presence_client_one.history do |history|
25
- expect(history.count).to eql(2)
24
+ presence_client_one.history do |history_page|
25
+ expect(history_page.items.count).to eql(2)
26
26
 
27
- expect(history[1].action).to eq(:enter)
28
- expect(history[1].client_id).to eq(client_one.client_id)
29
- expect(history[1].data).to eql(data)
27
+ expect(history_page.items[1].action).to eq(:enter)
28
+ expect(history_page.items[1].client_id).to eq(client_one.client_id)
29
+ expect(history_page.items[1].data).to eql(data)
30
30
 
31
- expect(history[0].action).to eq(:leave)
32
- expect(history[0].client_id).to eq(client_one.client_id)
33
- expect(history[0].data).to eql(leave_data)
31
+ expect(history_page.items[0].action).to eq(:leave)
32
+ expect(history_page.items[0].client_id).to eq(client_one.client_id)
33
+ expect(history_page.items[0].data).to eql(leave_data)
34
34
 
35
35
  stop_reactor
36
36
  end
@@ -40,16 +40,59 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
40
40
 
41
41
  it 'ensures REST presence history message IDs match ProtocolMessage wrapped message and connection IDs via Realtime' do
42
42
  presence_client_one.subscribe(:enter) do |message|
43
- presence_client_one.history do |history|
44
- expect(history.count).to eql(1)
43
+ presence_client_one.history do |history_page|
44
+ expect(history_page.items.count).to eql(1)
45
45
 
46
- expect(history[0].id).to eql(message.id)
47
- expect(history[0].connection_id).to eql(message.connection_id)
46
+ expect(history_page.items[0].id).to eql(message.id)
47
+ expect(history_page.items[0].connection_id).to eql(message.connection_id)
48
48
  stop_reactor
49
49
  end
50
50
  end
51
51
 
52
52
  presence_client_one.enter(data: data)
53
53
  end
54
+
55
+ context 'with option until_attach: true' do
56
+ let(:event) { random_str }
57
+ let(:presence_data_before_attach) { random_str }
58
+ let(:presence_data_after_attach) { random_str }
59
+
60
+ it 'retrieves all presence messages before channel was attached' do
61
+ presence_client_two.enter(data: presence_data_before_attach) do
62
+ presence_client_one.enter(data: presence_data_after_attach) do
63
+ presence_client_one.history(until_attach: true) do |presence_page|
64
+ expect(presence_page.items.count).to eql(1)
65
+ expect(presence_page.items.first.data).to eql(presence_data_before_attach)
66
+ stop_reactor
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ context 'and two pages of messages' do
73
+ it 'retrieves two pages of messages before channel was attached' do
74
+ when_all(*10.times.map { |i| presence_client_two.enter_client("client:#{i}", data: presence_data_before_attach) }) do
75
+ when_all(*10.times.map { |i| presence_client_one.enter_client("client:#{i}", data: presence_data_after_attach) }) do
76
+ presence_client_one.history(until_attach: true, limit: 5) do |presence_page|
77
+ expect(presence_page.items.count).to eql(5)
78
+ expect(presence_page.items.map(&:data).uniq.first).to eql(presence_data_before_attach)
79
+
80
+ presence_page.next do |presence_next_page|
81
+ expect(presence_next_page.items.count).to eql(5)
82
+ expect(presence_next_page.items.map(&:data).uniq.first).to eql(presence_data_before_attach)
83
+ expect(presence_next_page).to be_last
84
+ stop_reactor
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ it 'raises an exception unless state is attached' do
93
+ expect { presence_client_one.history(until_attach: true) }.to raise_error(ArgumentError, /not attached/)
94
+ stop_reactor
95
+ end
96
+ end
54
97
  end
55
98
  end
@@ -5,7 +5,7 @@ describe Ably::Realtime::Presence, :event_machine do
5
5
  include Ably::Modules::Conversions
6
6
 
7
7
  vary_by_protocol do
8
- let(:default_options) { { api_key: api_key, environment: environment, protocol: protocol } }
8
+ let(:default_options) { { key: api_key, environment: environment, protocol: protocol } }
9
9
  let(:client_options) { default_options }
10
10
 
11
11
  let(:anonymous_client) { Ably::Realtime::Client.new(client_options) }
@@ -487,7 +487,7 @@ describe Ably::Realtime::Presence, :event_machine do
487
487
 
488
488
  context 'without necessary capabilities to join presence' do
489
489
  let(:restricted_client) do
490
- Ably::Realtime::Client.new(default_options.merge(api_key: restricted_api_key, log_level: :fatal))
490
+ Ably::Realtime::Client.new(default_options.merge(key: restricted_api_key, log_level: :fatal))
491
491
  end
492
492
  let(:restricted_channel) { restricted_client.channel("cansubscribe:channel") }
493
493
  let(:restricted_presence) { restricted_channel.presence }
@@ -688,7 +688,7 @@ describe Ably::Realtime::Presence, :event_machine do
688
688
 
689
689
  context 'without necessary capabilities to enter on behalf of another client' do
690
690
  let(:restricted_client) do
691
- Ably::Realtime::Client.new(default_options.merge(api_key: restricted_api_key, log_level: :fatal))
691
+ Ably::Realtime::Client.new(default_options.merge(key: restricted_api_key, log_level: :fatal))
692
692
  end
693
693
  let(:restricted_channel) { restricted_client.channel("cansubscribe:channel") }
694
694
  let(:restricted_presence) { restricted_channel.presence }
@@ -1123,8 +1123,8 @@ describe Ably::Realtime::Presence, :event_machine do
1123
1123
  context 'REST #get' do
1124
1124
  it 'returns current members' do
1125
1125
  presence_client_one.enter(data: data_payload) do
1126
- members = channel_rest_client_one.presence.get
1127
- this_member = members.first
1126
+ members_page = channel_rest_client_one.presence.get
1127
+ this_member = members_page.items.first
1128
1128
 
1129
1129
  expect(this_member).to be_a(Ably::Models::PresenceMessage)
1130
1130
  expect(this_member.client_id).to eql(client_one.client_id)
@@ -1137,8 +1137,8 @@ describe Ably::Realtime::Presence, :event_machine do
1137
1137
  it 'returns no members once left' do
1138
1138
  presence_client_one.enter(data: data_payload) do
1139
1139
  presence_client_one.leave do
1140
- members = channel_rest_client_one.presence.get
1141
- expect(members.count).to eql(0)
1140
+ members_page = channel_rest_client_one.presence.get
1141
+ expect(members_page.items.count).to eql(0)
1142
1142
  stop_reactor
1143
1143
  end
1144
1144
  end
@@ -1264,7 +1264,7 @@ describe Ably::Realtime::Presence, :event_machine do
1264
1264
  context 'REST #get' do
1265
1265
  it 'returns a list of members with decrypted data' do
1266
1266
  encrypted_channel.presence.enter(data: data) do
1267
- member = channel_rest_client_one.presence.get.first
1267
+ member = channel_rest_client_one.presence.get.items.first
1268
1268
  expect(member.encoding).to be_nil
1269
1269
  expect(member.data).to eql(data)
1270
1270
  stop_reactor
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Ably::Realtime::Client, '#stats', :event_machine do
4
4
  vary_by_protocol do
5
5
  let(:client) do
6
- Ably::Realtime::Client.new(api_key: api_key, environment: environment, protocol: protocol)
6
+ Ably::Realtime::Client.new(key: api_key, environment: environment, protocol: protocol)
7
7
  end
8
8
 
9
9
  describe 'fetching stats' do
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Ably::Realtime::Client, '#time', :event_machine do
4
4
  vary_by_protocol do
5
5
  let(:client) do
6
- Ably::Realtime::Client.new(api_key: api_key, environment: environment, protocol: protocol)
6
+ Ably::Realtime::Client.new(key: api_key, environment: environment, protocol: protocol)
7
7
  end
8
8
 
9
9
  describe 'fetching the service time' do
@@ -4,17 +4,17 @@ require 'spec_helper'
4
4
  describe Ably::Auth do
5
5
  include Ably::Modules::Conversions
6
6
 
7
- def hmac_for(token_request, secret)
8
- ruby_named_token_request = Ably::Models::IdiomaticRubyWrapper.new(token_request)
7
+ def hmac_for(token_request_attributes, secret)
8
+ token_request= Ably::Models::IdiomaticRubyWrapper.new(token_request_attributes)
9
9
 
10
10
  text = [
11
- :id,
11
+ :key_name,
12
12
  :ttl,
13
13
  :capability,
14
14
  :client_id,
15
15
  :timestamp,
16
16
  :nonce
17
- ].map { |key| "#{ruby_named_token_request[key]}\n" }.join("")
17
+ ].map { |key| "#{token_request.hash[key]}\n" }.join("")
18
18
 
19
19
  encode64(
20
20
  OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, secret, text)
@@ -23,7 +23,7 @@ describe Ably::Auth do
23
23
 
24
24
  vary_by_protocol do
25
25
  let(:client) do
26
- Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
26
+ Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol)
27
27
  end
28
28
  let(:auth) { client.auth }
29
29
  let(:content_type) do
@@ -52,37 +52,43 @@ describe Ably::Auth do
52
52
  end
53
53
 
54
54
  it 'has immutable options' do
55
- expect { auth.options['key_id'] = 'new_id' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
55
+ expect { auth.options['key_name'] = 'new_name' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
56
56
  end
57
57
 
58
58
  describe '#request_token' do
59
59
  let(:ttl) { 30 * 60 }
60
60
  let(:capability) { { :foo => ['publish'] } }
61
61
 
62
- let(:token) do
62
+ let(:token_details) do
63
63
  auth.request_token(
64
64
  ttl: ttl,
65
65
  capability: capability
66
66
  )
67
67
  end
68
68
 
69
- it 'returns a valid requested token in the expected format with valid issued_at and expires_at attributes' do
70
- expect(token.id).to match(/^#{app_id}\.[\w-]+$/)
71
- expect(token.key_id).to match(/^#{key_id}$/)
72
- expect(token.issued_at).to be_within(2).of(Time.now)
73
- expect(token.expires_at).to be_within(2).of(Time.now + ttl)
69
+ it 'returns a valid requested token in the expected format with valid issued and expires attributes' do
70
+ expect(token_details.token).to match(/^#{app_id}\.[\w-]+$/)
71
+ expect(token_details.key_name).to match(/^#{key_name}$/)
72
+ expect(token_details.issued).to be_within(2).of(Time.now)
73
+ expect(token_details.expires).to be_within(2).of(Time.now + ttl)
74
74
  end
75
75
 
76
76
  %w(client_id capability nonce timestamp ttl).each do |option|
77
77
  context "with option :#{option}", :webmock do
78
- let(:random) { random_int_str }
78
+ def coerce_if_time_value(field_name, value, options = {})
79
+ multiply = options[:multiply]
80
+ return value unless %w(timestamp ttl).include?(field_name)
81
+ value.to_i * (multiply ? multiply : 1)
82
+ end
83
+
84
+ let(:random) { coerce_if_time_value(option, random_int_str) }
79
85
  let(:options) { { option.to_sym => random } }
80
86
 
81
- let(:token_response) { { access_token: {} } }
87
+ let(:token_response) { {} }
82
88
  let!(:request_token_stub) do
83
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
89
+ stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
84
90
  with do |request|
85
- request_body_includes(request, protocol, option, random)
91
+ request_body_includes(request, protocol, option, coerce_if_time_value(option, random, multiply: 1000))
86
92
  end.to_return(
87
93
  :status => 201,
88
94
  :body => serialize(token_response, protocol),
@@ -92,25 +98,25 @@ describe Ably::Auth do
92
98
 
93
99
  before { auth.request_token options }
94
100
 
95
- it 'overrides default and uses camelCase notation for all attributes' do
101
+ it "overrides default and uses camelCase notation for attributes" do
96
102
  expect(request_token_stub).to have_been_requested
97
103
  end
98
104
  end
99
105
  end
100
106
 
101
- context 'with :key_id & :key_secret options', :webmock do
102
- let(:key_id) { random_str }
107
+ context 'with :key option', :webmock do
108
+ let(:key_name) { "app.#{random_str}" }
103
109
  let(:key_secret) { random_str }
104
110
  let(:nonce) { random_str }
105
- let(:token_options) { { key_id: key_id, key_secret: key_secret, nonce: nonce, timestamp: Time.now } }
111
+ let(:token_options) { { key: "#{key_name}:#{key_secret}", nonce: nonce, timestamp: Time.now } }
106
112
  let(:token_request) { auth.create_token_request(token_options) }
107
113
  let(:mac) do
108
114
  hmac_for(token_request, key_secret)
109
115
  end
110
116
 
111
- let(:token_response) { { access_token: {} } }
117
+ let(:token_response) { {} }
112
118
  let!(:request_token_stub) do
113
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
119
+ stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
114
120
  with do |request|
115
121
  request_body_includes(request, protocol, 'mac', mac)
116
122
  end.to_return(
@@ -119,9 +125,38 @@ describe Ably::Auth do
119
125
  :headers => { 'Content-Type' => content_type })
120
126
  end
121
127
 
122
- let!(:token) { auth.request_token(token_options) }
128
+ let!(:token) { puts token_options; auth.request_token(token_options) }
123
129
 
124
- specify 'key_id is used in request and signing uses key_secret' do
130
+ specify 'key_name is used in request and signing uses key_secret' do
131
+ expect(request_token_stub).to have_been_requested
132
+ end
133
+ end
134
+
135
+ context 'with :key_name & :key_secret options', :webmock do
136
+ let(:key_name) { "app.#{random_str}" }
137
+ let(:key_secret) { random_str }
138
+ let(:nonce) { random_str }
139
+
140
+ let(:name_secret_token_options) { { key_name: key_name, key_secret: key_secret, nonce: nonce, timestamp: Time.now } }
141
+ let(:token_request) { auth.create_token_request(name_secret_token_options) }
142
+ let(:mac) do
143
+ hmac_for(token_request, key_secret)
144
+ end
145
+
146
+ let(:token_response) { {} }
147
+ let!(:request_token_stub) do
148
+ stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
149
+ with do |request|
150
+ request_body_includes(request, protocol, 'mac', mac)
151
+ end.to_return(
152
+ :status => 201,
153
+ :body => serialize(token_response, protocol),
154
+ :headers => { 'Content-Type' => content_type })
155
+ end
156
+
157
+ let!(:token) { auth.request_token(name_secret_token_options); }
158
+
159
+ specify 'key_name is used in request and signing uses key_secret' do
125
160
  expect(request_token_stub).to have_been_requested
126
161
  end
127
162
  end
@@ -146,8 +181,8 @@ describe Ably::Auth do
146
181
 
147
182
  context 'with :auth_url option', :webmock do
148
183
  let(:auth_url) { 'https://www.fictitious.com/get_token' }
149
- let(:auth_url_response) { { id: key_id } }
150
- let(:token_response) { { access_token: { } } }
184
+ let(:auth_url_response) { { keyName: key_name }.to_json }
185
+ let(:token_response) { {} }
151
186
  let(:query_params) { nil }
152
187
  let(:headers) { nil }
153
188
  let(:auth_method) { :get }
@@ -166,15 +201,16 @@ describe Ably::Auth do
166
201
  stub.with(:headers => headers) unless headers.nil?
167
202
  stub.to_return(
168
203
  :status => 201,
169
- :body => auth_url_response.to_json,
170
- :headers => { 'Content-Type' => 'application/json' }
204
+ :body => auth_url_response,
205
+ :headers => { 'Content-Type' => auth_url_content_type }
171
206
  )
172
207
  end
208
+ let(:auth_url_content_type) { 'application/json' }
173
209
 
174
210
  let!(:request_token_stub) do
175
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
211
+ stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
176
212
  with do |request|
177
- request_body_includes(request, protocol, 'id', key_id)
213
+ request_body_includes(request, protocol, 'key_name', key_name)
178
214
  end.to_return(
179
215
  :status => 201,
180
216
  :body => serialize(token_response, protocol),
@@ -191,7 +227,7 @@ describe Ably::Auth do
191
227
  end
192
228
 
193
229
  it 'returns a valid token generated from the token request' do
194
- expect(token).to be_a(Ably::Models::Token)
230
+ expect(token).to be_a(Ably::Models::TokenDetails)
195
231
  end
196
232
 
197
233
  context 'with :query_params' do
@@ -220,29 +256,45 @@ describe Ably::Auth do
220
256
  end
221
257
  end
222
258
 
223
- context 'when response from :auth_url is a token' do
224
- let(:token_id) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
225
- let(:issued_at) { Time.now }
259
+ context 'when response from :auth_url is a token details object' do
260
+ let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
261
+ let(:issued) { Time.now }
226
262
  let(:expires) { Time.now + 60}
227
263
  let(:capability) { {'foo'=>['publish']} }
264
+ let(:capability_str) { JSON.dump(capability) }
228
265
  let(:auth_url_response) do
229
266
  {
230
- 'id' => token_id,
231
- 'key' => 'J_0Tlg.NxCRig',
232
- 'issued_at' => issued_at.to_i,
233
- 'expires' => expires.to_i,
234
- 'capability'=> capability
235
- }
267
+ 'token' => token,
268
+ 'key_name' => 'J_0Tlg.NxCRig',
269
+ 'issued' => issued.to_i * 1000,
270
+ 'expires' => expires.to_i * 1000,
271
+ 'capability'=> capability_str
272
+ }.to_json
236
273
  end
237
274
 
238
- let!(:token) { auth.request_token(options) }
275
+ let!(:token_details) { auth.request_token(options) }
239
276
 
240
- it 'returns a Token created from the token JSON' do
277
+ it 'returns TokenDetails created from the token JSON' do
241
278
  expect(request_token_stub).to_not have_been_requested
242
- expect(token.id).to eql(token_id)
243
- expect(token.expires_at).to be_within(1).of(expires)
244
- expect(token.issued_at).to be_within(1).of(issued_at)
245
- expect(token.capability.to_json).to eql(capability.to_json)
279
+ expect(token_details).to be_a(Ably::Models::TokenDetails)
280
+ expect(token_details.token).to eql(token)
281
+ expect(token_details.expires).to be_within(1).of(expires)
282
+ expect(token_details.issued).to be_within(1).of(issued)
283
+ expect(token_details.capability).to eql(capability)
284
+ end
285
+ end
286
+
287
+ context 'when response from :auth_url is text/plain content type and a token string' do
288
+ let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
289
+ let(:auth_url_content_type) { 'text/plain' }
290
+ let(:auth_url_response) { token }
291
+
292
+ let!(:token_details) { auth.request_token(options) }
293
+
294
+ it 'returns TokenDetails created from the token JSON' do
295
+ expect(request_token_stub).to_not have_been_requested
296
+ expect(token_details).to be_a(Ably::Models::TokenDetails)
297
+ expect(token_details.token).to eql(token)
246
298
  end
247
299
  end
248
300
 
@@ -270,90 +322,135 @@ describe Ably::Auth do
270
322
  end
271
323
  end
272
324
 
273
- context 'with token_request_block that returns a token request' do
274
- let(:client_id) { random_str }
275
- let(:options) { { client_id: client_id } }
276
- let!(:request_token) do
277
- auth.request_token(options) do |block_options|
278
- @block_called = true
279
- @block_options = block_options
280
- auth.create_token_request(client_id: client_id)
325
+ context 'with a Proc for the :auth_callback option' do
326
+ context 'that returns a TokenRequest' do
327
+ let(:client_id) { random_str }
328
+ let(:options) { { client_id: client_id } }
329
+ let!(:request_token) do
330
+ auth.request_token(options.merge(auth_callback: Proc.new do |block_options|
331
+ @block_called = true
332
+ @block_options = block_options
333
+ auth.create_token_request(client_id: client_id)
334
+ end))
281
335
  end
282
- end
283
336
 
284
- it 'calls the block when authenticating to obtain the request token' do
285
- expect(@block_called).to eql(true)
286
- expect(@block_options).to include(options)
287
- end
337
+ it 'calls the Proc when authenticating to obtain the request token' do
338
+ expect(@block_called).to eql(true)
339
+ expect(@block_options).to include(options)
340
+ end
288
341
 
289
- it 'uses the token request from the block when requesting a new token' do
290
- expect(request_token.client_id).to eql(client_id)
342
+ it 'uses the token request returned from the callback when requesting a new token' do
343
+ expect(request_token.client_id).to eql(client_id)
344
+ end
291
345
  end
292
- end
293
346
 
294
- context 'with token_request_block that returns a token' do
295
- let(:client_id) { random_str }
296
- let(:options) { { client_id: client_id } }
297
- let(:token_id) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
298
- let(:issued_at) { Time.now }
299
- let(:expires) { Time.now + 60}
300
- let(:capability) { {'foo'=>['publish']} }
347
+ context 'that returns a TokenDetails JSON object' do
348
+ let(:client_id) { random_str }
349
+ let(:options) { { client_id: client_id } }
350
+ let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
351
+ let(:issued) { Time.now }
352
+ let(:expires) { Time.now + 60}
353
+ let(:capability) { {'foo'=>['publish']} }
354
+ let(:capability_str) { JSON.dump(capability) }
355
+
356
+ let!(:token_details) do
357
+ auth.request_token(options.merge(auth_callback: Proc.new do |block_options|
358
+ @block_called = true
359
+ @block_options = block_options
360
+ {
361
+ 'token' => token,
362
+ 'keyName' => 'J_0Tlg.NxCRig',
363
+ 'clientId' => client_id,
364
+ 'issued' => issued.to_i * 1000,
365
+ 'expires' => expires.to_i * 1000,
366
+ 'capability'=> capability_str
367
+ }
368
+ end))
369
+ end
301
370
 
302
- let!(:request_token) do
303
- auth.request_token(options) do |block_options|
304
- @block_called = true
305
- @block_options = block_options
306
- {
307
- 'id' => token_id,
308
- 'key' => 'J_0Tlg.NxCRig',
309
- 'client_id' => client_id,
310
- 'issued_at' => issued_at.to_i,
311
- 'expires' => expires.to_i,
312
- 'capability'=> capability
313
- }
371
+ it 'calls the Proc when authenticating to obtain the request token' do
372
+ expect(@block_called).to eql(true)
373
+ expect(@block_options).to include(options)
374
+ end
375
+
376
+ it 'uses the token request returned from the callback when requesting a new token' do
377
+ expect(token_details).to be_a(Ably::Models::TokenDetails)
378
+ expect(token_details.token).to eql(token)
379
+ expect(token_details.client_id).to eql(client_id)
380
+ expect(token_details.expires).to be_within(1).of(expires)
381
+ expect(token_details.issued).to be_within(1).of(issued)
382
+ expect(token_details.capability).to eql(capability)
314
383
  end
315
384
  end
316
385
 
317
- it 'calls the block when authenticating to obtain the request token' do
318
- expect(@block_called).to eql(true)
319
- expect(@block_options).to include(options)
386
+ context 'that returns a TokenDetails object' do
387
+ let(:client_id) { random_str }
388
+
389
+ let!(:token_details) do
390
+ auth.request_token(auth_callback: Proc.new do |block_options|
391
+ auth.create_token_request({
392
+ client_id: client_id
393
+ })
394
+ end)
395
+ end
396
+
397
+ it 'uses the token request returned from the callback when requesting a new token' do
398
+ expect(token_details).to be_a(Ably::Models::TokenDetails)
399
+ expect(token_details.client_id).to eql(client_id)
400
+ end
320
401
  end
321
402
 
322
- it 'uses the token request from the block when requesting a new token' do
323
- expect(request_token).to be_a(Ably::Models::Token)
324
- expect(request_token.id).to eql(token_id)
325
- expect(request_token.client_id).to eql(client_id)
326
- expect(request_token.expires_at).to be_within(1).of(expires)
327
- expect(request_token.issued_at).to be_within(1).of(issued_at)
328
- expect(request_token.capability.to_json).to eql(capability.to_json)
403
+ context 'that returns a Token string' do
404
+ let(:second_client) { Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol) }
405
+ let(:token) { second_client.auth.request_token.token }
406
+
407
+ let!(:token_details) do
408
+ auth.request_token(auth_callback: Proc.new do |block_options|
409
+ token
410
+ end)
411
+ end
412
+
413
+ it 'uses the token request returned from the callback when requesting a new token' do
414
+ expect(token_details).to be_a(Ably::Models::TokenDetails)
415
+ expect(token_details.token).to eql(token)
416
+ end
329
417
  end
330
418
  end
331
419
 
332
420
  context 'persisted option', api_private: true do
333
421
  context 'when set to true', api_private: true do
334
422
  let(:options) { { persisted: true } }
335
- let(:token) { auth.request_token(options) }
423
+ let(:token_details) { auth.request_token(options) }
336
424
 
337
425
  it 'returns a token with a short token ID that is used to look up the token details' do
338
- expect(token.id.length).to be < 64
339
- expect(token.id).to match(/^#{app_id}\.A/)
426
+ expect(token_details.token.length).to be < 64
427
+ expect(token_details.token).to match(/^#{app_id}\.A/)
340
428
  end
341
429
  end
342
430
 
343
431
  context 'when omitted', api_private: true do
344
432
  let(:options) { { } }
345
- let(:token) { auth.request_token(options) }
433
+ let(:token_details) { auth.request_token(options) }
346
434
 
347
435
  it 'returns a literal token' do
348
- expect(token.id.length).to be > 64
436
+ expect(token_details.token.length).to be > 64
349
437
  end
350
438
  end
351
439
  end
440
+
441
+ context 'with client_id' do
442
+ let(:client_id) { random_str }
443
+ let(:token_details) { auth.request_token(client_id: client_id) }
444
+
445
+ it 'returns a token with the client_id' do
446
+ expect(token_details.client_id).to eql(client_id)
447
+ end
448
+ end
352
449
  end
353
450
 
354
451
  context 'before #authorise has been called' do
355
- it 'has no current_token' do
356
- expect(auth.current_token).to be_nil
452
+ it 'has no current_token_details' do
453
+ expect(auth.current_token_details).to be_nil
357
454
  end
358
455
  end
359
456
 
@@ -369,62 +466,62 @@ describe Ably::Auth do
369
466
  end
370
467
 
371
468
  it 'returns a valid token' do
372
- expect(auth.authorise).to be_a(Ably::Models::Token)
469
+ expect(auth.authorise).to be_a(Ably::Models::TokenDetails)
373
470
  end
374
471
 
375
472
  it 'issues a new token if option :force => true' do
376
- expect { auth.authorise(force: true) }.to change { auth.current_token }
473
+ expect { auth.authorise(force: true) }.to change { auth.current_token_details }
377
474
  end
378
475
  end
379
476
 
380
477
  context 'with previous authorisation' do
381
478
  before do
382
479
  auth.authorise
383
- expect(auth.current_token).to_not be_expired
480
+ expect(auth.current_token_details).to_not be_expired
384
481
  end
385
482
 
386
- it 'does not request a token if current_token has not expired' do
483
+ it 'does not request a token if current_token_details has not expired' do
387
484
  expect(auth).to_not receive(:request_token)
388
485
  auth.authorise
389
486
  end
390
487
 
391
488
  it 'requests a new token if token is expired' do
392
- allow(auth.current_token).to receive(:expired?).and_return(true)
489
+ allow(auth.current_token_details).to receive(:expired?).and_return(true)
393
490
  expect(auth).to receive(:request_token)
394
- expect { auth.authorise }.to change { auth.current_token }
491
+ expect { auth.authorise }.to change { auth.current_token_details }
395
492
  end
396
493
 
397
494
  it 'issues a new token if option :force => true' do
398
- expect { auth.authorise(force: true) }.to change { auth.current_token }
495
+ expect { auth.authorise(force: true) }.to change { auth.current_token_details }
399
496
  end
400
497
  end
401
498
 
402
- it 'updates the persisted auth options thare are then used for subsequent authorise requests' do
499
+ it 'updates the persisted auth options that are then used for subsequent authorise requests' do
403
500
  expect(auth.options[:ttl]).to_not eql(26)
404
501
  auth.authorise(ttl: 26)
405
502
  expect(auth.options[:ttl]).to eql(26)
406
503
  end
407
504
 
408
- context 'with token_request_block' do
505
+ context 'with a Proc for the :auth_callback option' do
409
506
  let(:client_id) { random_str }
410
507
  let!(:token) do
411
- auth.authorise do
508
+ auth.authorise(auth_callback: Proc.new do
412
509
  @block_called ||= 0
413
510
  @block_called += 1
414
511
  auth.create_token_request(client_id: client_id)
415
- end
512
+ end)
416
513
  end
417
514
 
418
- it 'calls the block' do
515
+ it 'calls the Proc' do
419
516
  expect(@block_called).to eql(1)
420
517
  end
421
518
 
422
- it 'uses the token request returned from the block when requesting a new token' do
519
+ it 'uses the token request returned from the callback when requesting a new token' do
423
520
  expect(token.client_id).to eql(client_id)
424
521
  end
425
522
 
426
523
  context 'for every subsequent #request_token' do
427
- context 'without a provided block' do
524
+ context 'without a :auth_callback Proc' do
428
525
  it 'calls the originally provided block' do
429
526
  auth.request_token
430
527
  expect(@block_called).to eql(2)
@@ -432,8 +529,8 @@ describe Ably::Auth do
432
529
  end
433
530
 
434
531
  context 'with a provided block' do
435
- it 'does not call the originally provided block and calls the new #request_token block' do
436
- auth.request_token { @request_block_called = true; auth.create_token_request }
532
+ it 'does not call the originally provided Proc and calls the new #request_token :auth_callback Proc' do
533
+ auth.request_token(auth_callback: Proc.new { @request_block_called = true; auth.create_token_request })
437
534
  expect(@block_called).to eql(1)
438
535
  expect(@request_block_called).to eql(true)
439
536
  end
@@ -445,19 +542,19 @@ describe Ably::Auth do
445
542
  describe '#create_token_request' do
446
543
  let(:ttl) { 60 * 60 }
447
544
  let(:capability) { { :foo => ["publish"] } }
448
- let(:options) { Hash.new }
449
- subject { auth.create_token_request(options) }
545
+ let(:token_request_options) { Hash.new }
546
+ subject { auth.create_token_request(token_request_options) }
450
547
 
451
- it 'uses the key ID from the client' do
452
- expect(subject['id']).to eql(key_id)
548
+ it 'uses the key name from the client' do
549
+ expect(subject['keyName']).to eql(key_name)
453
550
  end
454
551
 
455
552
  it 'uses the default TTL' do
456
- expect(subject['ttl']).to eql(Ably::Models::Token::DEFAULTS[:ttl])
553
+ expect(subject['ttl']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl) * 1000)
457
554
  end
458
555
 
459
556
  it 'uses the default capability' do
460
- expect(subject['capability']).to eql(Ably::Models::Token::DEFAULTS[:capability].to_json)
557
+ expect(subject['capability']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:capability).to_json)
461
558
  end
462
559
 
463
560
  context 'the nonce' do
@@ -471,25 +568,25 @@ describe Ably::Auth do
471
568
  end
472
569
  end
473
570
 
474
- %w(ttl capability nonce timestamp client_id).each do |attribute|
571
+ %w(ttl nonce client_id).each do |attribute|
475
572
  context "with option :#{attribute}" do
476
- let(:option_value) { random_int_str(1_000_000_000) }
573
+ let(:option_value) { random_int_str(1_000_000_000).to_i }
477
574
  before do
478
- options[attribute.to_sym] = option_value
575
+ token_request_options[attribute.to_sym] = option_value
479
576
  end
480
577
  it "overrides default" do
481
- expect(subject[convert_to_mixed_case(attribute)].to_s).to eql(option_value.to_s)
578
+ expect(subject.public_send(attribute).to_s).to eql(option_value.to_s)
482
579
  end
483
580
  end
484
581
  end
485
582
 
486
583
  context 'with additional invalid attributes' do
487
- let(:options) { { nonce: 'valid', is_not_used_by_token_request: 'invalid' } }
584
+ let(:token_request_options) { { nonce: 'valid', is_not_used_by_token_request: 'invalid' } }
488
585
  specify 'are ignored' do
489
- expect(subject.keys).to_not include(:is_not_used_by_token_request)
490
- expect(subject.keys).to_not include(convert_to_mixed_case(:is_not_used_by_token_request))
491
- expect(subject.keys).to include('nonce')
492
- expect(subject['nonce']).to eql('valid')
586
+ expect(subject.hash.keys).to_not include(:is_not_used_by_token_request)
587
+ expect(subject.hash.keys).to_not include(convert_to_mixed_case(:is_not_used_by_token_request))
588
+ expect(subject.hash.keys).to include(:nonce)
589
+ expect(subject.nonce).to eql('valid')
493
590
  end
494
591
  end
495
592
 
@@ -497,47 +594,52 @@ describe Ably::Auth do
497
594
  let(:client) { Ably::Rest::Client.new(auth_url: 'http://example.com', protocol: protocol) }
498
595
 
499
596
  it 'should raise an exception if key secret is missing' do
500
- expect { auth.create_token_request(key_id: 'id') }.to raise_error Ably::Exceptions::TokenRequestError
597
+ expect { auth.create_token_request(key_name: 'name') }.to raise_error Ably::Exceptions::TokenRequestError
501
598
  end
502
599
 
503
- it 'should raise an exception if key id is missing' do
600
+ it 'should raise an exception if key name is missing' do
504
601
  expect { auth.create_token_request(key_secret: 'secret') }.to raise_error Ably::Exceptions::TokenRequestError
505
602
  end
506
603
  end
507
604
 
508
605
  context 'with :query_time option' do
509
606
  let(:time) { Time.now - 30 }
510
- let(:options) { { query_time: true } }
607
+ let(:token_request_options) { { query_time: true } }
511
608
 
512
609
  it 'queries the server for the timestamp' do
513
610
  expect(client).to receive(:time).and_return(time)
514
- expect(subject['timestamp']).to eql(time.to_i)
611
+ expect(subject['timestamp']).to be_within(1).of(time.to_f * 1000)
515
612
  end
516
613
  end
517
614
 
518
615
  context 'with :timestamp option' do
519
616
  let(:token_request_time) { Time.now + 5 }
520
- let(:options) { { timestamp: token_request_time } }
617
+ let(:token_request_options) { { timestamp: token_request_time } }
521
618
 
522
619
  it 'uses the provided timestamp in the token request' do
523
- expect(subject['timestamp']).to eql(token_request_time.to_i)
620
+ expect(subject['timestamp']).to be_within(1).of(token_request_time.to_f * 1000)
524
621
  end
525
622
  end
526
623
 
527
624
  context 'signing' do
528
- let(:options) do
625
+ let(:token_request_options) do
529
626
  {
530
- id: random_str,
531
- ttl: random_str,
627
+ key_name: random_str,
628
+ ttl: random_int_str.to_i,
532
629
  capability: random_str,
533
630
  client_id: random_str,
534
- timestamp: random_int_str,
631
+ timestamp: random_int_str.to_i,
535
632
  nonce: random_str
536
633
  }
537
634
  end
538
635
 
636
+ # TokenRequest expects times in milliseconds, whereas create_token_request assumes Ruby default of seconds
637
+ let(:token_request_attributes) do
638
+ token_request_options.merge(timestamp: token_request_options[:timestamp] * 1000, ttl: token_request_options[:ttl] * 1000)
639
+ end
640
+
539
641
  it 'generates a valid HMAC' do
540
- hmac = hmac_for(options, key_secret)
642
+ hmac = hmac_for(Ably::Models::TokenRequest(token_request_attributes).hash, key_secret)
541
643
  expect(subject['mac']).to eql(hmac)
542
644
  end
543
645
  end
@@ -546,20 +648,20 @@ describe Ably::Auth do
546
648
  context 'using token authentication' do
547
649
  let(:capability) { { :foo => ["publish"] } }
548
650
 
549
- describe 'with :token_id option' do
651
+ describe 'with :token option' do
550
652
  let(:ttl) { 60 * 60 }
551
- let(:token) do
653
+ let(:token_details) do
552
654
  auth.request_token(
553
655
  ttl: ttl,
554
656
  capability: capability
555
657
  )
556
658
  end
557
- let(:token_id) { token.id }
659
+ let(:token) { token_details.token }
558
660
  let(:token_auth_client) do
559
- Ably::Rest::Client.new(token_id: token_id, environment: environment, protocol: protocol)
661
+ Ably::Rest::Client.new(token: token, environment: environment, protocol: protocol)
560
662
  end
561
663
 
562
- it 'authenticates successfully using the provided :token_id' do
664
+ it 'authenticates successfully using the provided :token' do
563
665
  expect(token_auth_client.channel('foo').publish('event', 'data')).to be_truthy
564
666
  end
565
667
 
@@ -587,25 +689,23 @@ describe Ably::Auth do
587
689
  context 'when implicit as a result of using :client id' do
588
690
  let(:client_id) { '999' }
589
691
  let(:client) do
590
- Ably::Rest::Client.new(api_key: api_key, client_id: client_id, environment: environment, protocol: protocol)
692
+ Ably::Rest::Client.new(key: api_key, client_id: client_id, environment: environment, protocol: protocol)
591
693
  end
592
- let(:token_id) { 'unique-token-id' }
694
+ let(:token) { 'unique-token' }
593
695
  let(:token_response) do
594
696
  {
595
- access_token: {
596
- id: token_id
597
- }
697
+ token: token
598
698
  }.to_json
599
699
  end
600
700
 
601
701
  context 'and requests to the Ably server are mocked', :webmock do
602
702
  let!(:request_token_stub) do
603
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
703
+ stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
604
704
  to_return(:status => 201, :body => token_response, :headers => { 'Content-Type' => 'application/json' })
605
705
  end
606
706
  let!(:publish_message_stub) do
607
707
  stub_request(:post, "#{client.endpoint}/channels/foo/publish").
608
- with(headers: { 'Authorization' => "Bearer #{encode64(token_id)}" }).
708
+ with(headers: { 'Authorization' => "Bearer #{encode64(token)}" }).
609
709
  to_return(status: 201, body: '{}', headers: { 'Content-Type' => 'application/json' })
610
710
  end
611
711
 
@@ -616,7 +716,7 @@ describe Ably::Auth do
616
716
  end
617
717
 
618
718
  describe 'a token is created' do
619
- let(:token) { client.auth.current_token }
719
+ let(:token) { client.auth.current_token_details }
620
720
 
621
721
  it 'before a request is made' do
622
722
  expect(token).to be_nil
@@ -629,22 +729,26 @@ describe Ably::Auth do
629
729
  it 'with capability and TTL defaults' do
630
730
  client.channel('foo').publish('event', 'data')
631
731
 
632
- expect(token).to be_a(Ably::Models::Token)
633
- capability_with_str_key = Ably::Models::Token::DEFAULTS[:capability]
634
- capability = Hash[capability_with_str_key.keys.map(&:to_sym).zip(capability_with_str_key.values)]
732
+ expect(token).to be_a(Ably::Models::TokenDetails)
733
+ capability_with_str_key = Ably::Auth::TOKEN_DEFAULTS.fetch(:capability)
734
+ capability = Hash[capability_with_str_key.keys.map(&:to_s).zip(capability_with_str_key.values)]
635
735
  expect(token.capability).to eq(capability)
636
- expect(token.expires_at.to_i).to be_within(2).of(Time.now.to_i + Ably::Models::Token::DEFAULTS[:ttl])
736
+ expect(token.expires.to_i).to be_within(2).of(Time.now.to_i + Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl))
637
737
  expect(token.client_id).to eq(client_id)
638
738
  end
639
739
  end
640
740
  end
641
741
  end
642
742
 
643
- context 'when using an :api_key and basic auth' do
743
+ context 'when using an :key and basic auth' do
644
744
  specify '#using_token_auth? is false' do
645
745
  expect(auth).to_not be_using_token_auth
646
746
  end
647
747
 
748
+ specify '#key attribute contains the key string' do
749
+ expect(auth.key).to eql(api_key)
750
+ end
751
+
648
752
  specify '#using_basic_auth? is true' do
649
753
  expect(auth).to be_using_basic_auth
650
754
  end