Skip to content
This repository has been archived by the owner on Jun 14, 2024. It is now read-only.

Distinguish errors by Exception class #47

Merged
merged 10 commits into from
Jul 30, 2021
19 changes: 13 additions & 6 deletions lib/bing_translator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@

class BingTranslator
class Exception < StandardError; end
class ApiException < BingTranslator::Exception; end
class UsageException < BingTranslator::Exception; end
class UnavailableException < BingTranslator::Exception; end
class AuthenticationException < BingTranslator::Exception; end

class ApiClient
API_HOST = 'https://api.cognitive.microsofttranslator.com'.freeze
Expand Down Expand Up @@ -51,7 +55,7 @@ def json_response(uri, request)

response = http.request(request)

raise Exception.new("Unsuccessful API call: Code: #{response.code}") unless response.code == '200'
raise ApiException.new("Unsuccessful API call: Code: #{response.code}") unless response.code == '200'
JSON.parse(response.body)
end

Expand Down Expand Up @@ -88,11 +92,14 @@ def request_new_access_token
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @skip_ssl_verify

response = http.post(COGNITIVE_ACCESS_TOKEN_URI.path, '', headers)
if response.code != '200'
raise Exception.new("Unsuccessful Access Token call: Code: #{response.code} (Invalid credentials?)")
else
case response
when Net::HTTPSuccess
@access_token_expiration_time = Time.now + 480
response.body
when Net::HTTPServerError
raise UnavailableException.new("#{response.code}: Credentials server unavailable")
else
raise AuthenticationException.new("Unsuccessful Access Token call: Code: #{response.code} (Invalid credentials?)")
end
end
end
Expand Down Expand Up @@ -131,7 +138,7 @@ def detect(text)
end

def speak(text, params = {})
raise Exception.new('Not supported since 3.0.0')
raise UsageException.new('Not supported since 3.0.0')
end

def supported_language_codes
Expand All @@ -157,7 +164,7 @@ def language_names(codes, locale = 'en')
attr_reader :api_client

def translation_request(texts, params)
raise Exception.new('Must provide :to.') if params[:to].nil?
raise UsageException.new('Must provide :to.') if params[:to].nil?

data = texts.map { |text| { 'Text' => text } }.to_json
response_json = api_client.post('/translate', params: params, data: data)
Expand Down
78 changes: 76 additions & 2 deletions spec/bing_translator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
describe BingTranslator do
include RSpecHtmlMatchers

COGNITIVE_ACCESS_TOKEN_URI =
URI.parse('https://api.cognitive.microsoft.com/sts/v1.0/issueToken').freeze

def load_file(filename)
File.read(File.join(File.dirname(__FILE__), 'etc', filename))
end
Expand Down Expand Up @@ -67,14 +70,82 @@ def load_file(filename)
expect(result).to eq message_en
end

context 'when the authentication server is offline' do
# Use a new, non-cached translator so that we guarantee hitting the authentication server
let(:authenticating_translator) { described_class.new(api_key, skip_ssl_verify: false) }

it 'throws a reasonable error when a 500 error is encountered' do
stub_request(:any, COGNITIVE_ACCESS_TOKEN_URI).
danopia marked this conversation as resolved.
Show resolved Hide resolved
to_return(status: [500, 'Internal Server Error'])

expect { authenticating_translator.translate 'hola', from: :es, to: :en }
.to raise_error(BingTranslator::Exception)
expect { authenticating_translator.translate 'hola', from: :es, to: :en }
.to raise_error(BingTranslator::UnavailableException, '500: Credentials server unavailable')
end

it 'throws a reasonable error with a different 5XX error' do
stub_request(:any, COGNITIVE_ACCESS_TOKEN_URI).
to_return(status: [503, 'Service Unavailable'])

expect { authenticating_translator.translate 'hola', from: :es, to: :en }
.to raise_error(BingTranslator::Exception)
expect { authenticating_translator.translate 'hola', from: :es, to: :en }
.to raise_error(BingTranslator::UnavailableException, '503: Credentials server unavailable')
end
end

context 'when the authentication server is online' do
it 'throws an AuthenticationException if the authentication key is invalid' do
fake_api_key = '32'
authenticating_translator = BingTranslator.new(fake_api_key, skip_ssl_verify: false)

expect { authenticating_translator.translate 'hola', from: :es, to: :en }
.to raise_error(BingTranslator::Exception)
expect { authenticating_translator.translate 'hola', from: :es, to: :en }
.to raise_error(BingTranslator::AuthenticationException, "Unsuccessful Access Token call: Code: 401 (Invalid credentials?)")
end

it 'throws an AuthenticationException if HTTP response code is not 200 or 5XX' do
authenticating_translator = BingTranslator.new(api_key, skip_ssl_verify: false)
stub_request(:any, COGNITIVE_ACCESS_TOKEN_URI).
to_return(status: [404, 'Not Found'])

expect { authenticating_translator.translate 'hola', from: :es, to: :en }
.to raise_error(BingTranslator::Exception)
expect { authenticating_translator.translate 'hola', from: :es, to: :en }
.to raise_error(BingTranslator::AuthenticationException, "Unsuccessful Access Token call: Code: 404 (Invalid credentials?)")
end
end

context 'when invalid language is specified' do
it 'throws a reasonable error' do
expect { translator.translate 'hola', from: :invlaid, to: :en }
.to raise_error(BingTranslator::Exception)
expect { translator.translate 'hola', from: :invlaid, to: :en }
.to raise_error(BingTranslator::ApiException)
end
end

context 'when no target language is specified' do
it 'throws a reasonable error' do
expect { translator.translate 'hola', from: :es }
.to raise_error(BingTranslator::Exception)
expect { translator.translate 'hola', from: :es }
.to raise_error(BingTranslator::UsageException)
end
end
end

describe '#speak' do
it 'will always throw an exception' do
expect { translator.speak 'hola', from: :es, to: :en }
.to raise_error(BingTranslator::Exception)
expect { translator.speak 'hola', from: :es, to: :en }
.to raise_error(BingTranslator::UsageException)
end
end

describe '#translate_array' do
it 'translates array of texts' do
result = translator.translate_array [message_en, message_en_other], from: :en, to: :fr
Expand Down Expand Up @@ -134,13 +205,15 @@ def load_file(filename)

subject { translator.translate 'hola', from: :es, to: :en }

it 'throws a BingTranslator::Exception exception' do
it 'throws a BingTranslator::AuthenticationException exception' do
expect { subject }.to raise_error(BingTranslator::Exception)
expect { subject }.to raise_error(BingTranslator::AuthenticationException)
end

context 'trying to translate something twice' do
it 'throws the BingTranslator::Exception exception every time' do
it 'throws the BingTranslator::AuthenticationException exception every time' do
2.times { expect { subject }.to raise_error(BingTranslator::Exception) }
2.times { expect { subject }.to raise_error(BingTranslator::AuthenticationException) }
end
end
end
Expand Down Expand Up @@ -188,6 +261,7 @@ def action

it 'throws an error' do
expect { subject }.to raise_error(BingTranslator::Exception)
expect { subject }.to raise_error(BingTranslator::ApiException)
end
end

Expand Down