diff --git a/lib/ably/exceptions.rb b/lib/ably/exceptions.rb index de2633390..a5b548880 100644 --- a/lib/ably/exceptions.rb +++ b/lib/ably/exceptions.rb @@ -38,6 +38,7 @@ def to_s additional_info << "base exception: #{@base_exception.class}" if @base_exception additional_info << "request_id: #{request_id}" if request_id message << "(#{additional_info.join(', ')})" + message << "-> see https://help.ably.io/error/#{code} for help" if code end message.join(' ') end @@ -54,6 +55,8 @@ def as_json(*args) # An invalid request was received by Ably class InvalidRequest < BaseAblyException; end + class InvalidCredentials < BaseAblyException; end + # Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided class UnauthorizedRequest < BaseAblyException; end diff --git a/lib/ably/models/error_info.rb b/lib/ably/models/error_info.rb index 8981c0630..7b5494275 100644 --- a/lib/ably/models/error_info.rb +++ b/lib/ably/models/error_info.rb @@ -38,7 +38,7 @@ def initialize(hash_object) @hash_object = IdiomaticRubyWrapper(hash_object.clone.freeze) end - %w(message code status_code).each do |attribute| + %w(message code href status_code).each do |attribute| define_method attribute do attributes[attribute.to_sym] end @@ -50,7 +50,8 @@ def attributes end def to_s - "" + see_msg = " -> see https://help.ably.io/error/#{code} for help" if code + "#{see_msg}" end end end diff --git a/spec/acceptance/realtime/client_spec.rb b/spec/acceptance/realtime/client_spec.rb index 9df376e47..ff282dd72 100644 --- a/spec/acceptance/realtime/client_spec.rb +++ b/spec/acceptance/realtime/client_spec.rb @@ -24,6 +24,22 @@ end end + context 'with an invalid API key' do + let(:custom_logger_object) { TestLogger.new } + let(:client) { Ably::Realtime::Client.new(client_options.merge(key: 'app.key:secret', logger: custom_logger_object)) } + + it 'logs an entry with a help href url matching the code #TI5' do + client.connect + client.connection.once(:failed) do + expect(custom_logger_object.logs.find do |severity, message| + next unless %w(fatal error).include?(severity.to_s) + message.match(%r{https://help.ably.io/error/40400}) + end).to_not be_nil + stop_reactor + end + end + end + context ':tls option' do context 'set to false to force a plain-text connection' do let(:client_options) { default_options.merge(tls: false, log_level: :none) } diff --git a/spec/acceptance/rest/client_spec.rb b/spec/acceptance/rest/client_spec.rb index 575c74000..c7dcae556 100644 --- a/spec/acceptance/rest/client_spec.rb +++ b/spec/acceptance/rest/client_spec.rb @@ -27,6 +27,19 @@ def encode64(text) end end + context 'with an invalid API key' do + let(:client) { Ably::Rest::Client.new(client_options.merge(key: 'app.key:secret', log_level: :fatal)) } + + it 'logs an entry with a help href url matching the code #TI5' do + begin + client.channels.get('foo').publish('test') + raise 'Expected Ably::Exceptions::ResourceMissing' + rescue Ably::Exceptions::ResourceMissing => err + expect err.to_s.match(%r{https://help.ably.io/error/40400}) + end + end + end + context 'with an explicit string :token' do let(:client) { Ably::Rest::Client.new(client_options.merge(token: random_str)) } diff --git a/spec/unit/models/error_info_spec.rb b/spec/unit/models/error_info_spec.rb index 40c2a3653..4b3418f9b 100644 --- a/spec/unit/models/error_info_spec.rb +++ b/spec/unit/models/error_info_spec.rb @@ -4,15 +4,25 @@ describe Ably::Models::ErrorInfo do subject { Ably::Models::ErrorInfo } - it_behaves_like 'a model', with_simple_attributes: %w(code status_code message) do - let(:model_args) { [] } + context '#TI1, #TI4' do + it_behaves_like 'a model', with_simple_attributes: %w(code status_code href message) do + let(:model_args) { [] } + end end - context '#status' do + context '#status #TI1, #TI2' do subject { Ably::Models::ErrorInfo.new('statusCode' => 401) } it 'is an alias for #status_code' do expect(subject.status).to eql(subject.status_code) expect(subject.status).to eql(401) end end + + context 'log entries container help link #TI5' do + subject { Ably::Models::ErrorInfo.new('code' => 44444) } + + it 'includes https://help.ably.io/error/[CODE] in the stringified object' do + expect(subject.to_s).to include('https://help.ably.io/error/44444') + end + end end