Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/errors/twilio_errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module TwilioErrors
21_211 => I18n.t('errors.messages.invalid_phone_number'),
21_215 => I18n.t('errors.messages.invalid_calling_area'),
21_614 => I18n.t('errors.messages.invalid_sms_number'),
4_815_162_342 => I18n.t('errors.messages.twilio_timeout'),
}.freeze

VERIFY_ERRORS = {
Expand Down
20 changes: 15 additions & 5 deletions app/services/twilio_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Utils
end

def initialize
@http_client = Twilio::HTTP::Client.new(timeout: Figaro.env.twilio_timeout.to_i)
@client = if FeatureManagement.telephony_disabled?
NullTwilioClient.new
else
Expand Down Expand Up @@ -41,13 +42,10 @@ def from_number

private

attr_reader :client
attr_reader :client, :http_client

def twilio_client
telephony_service.new(
TWILIO_SID,
TWILIO_AUTH_TOKEN
)
telephony_service.new(TWILIO_SID, TWILIO_AUTH_TOKEN, nil, nil, @http_client)
end

def random_phone_number
Expand All @@ -59,6 +57,8 @@ def sanitize_errors
rescue Twilio::REST::RestError => error
sanitize_phone_number(error.message)
raise
rescue Faraday::TimeoutError
raise Twilio::REST::RestError.new('timeout', TwilioTimeoutResponse.new)
end

DIGITS_TO_PRESERVE = 5
Expand All @@ -72,5 +72,15 @@ def sanitize_phone_number(str)
end
end
end

class TwilioTimeoutResponse
def status_code
4_815_162_342
end

def body
{}
end
end
end
end
1 change: 1 addition & 0 deletions config/application.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ use_dashboard_service_providers: 'false'
dashboard_url: 'https://dashboard.demo.login.gov'
valid_authn_contexts: '["http://idmanagement.gov/ns/assurance/loa/1", "http://idmanagement.gov/ns/assurance/loa/3"]'

twilio_timeout: '5'
usps_upload_sftp_timeout: '5'

development:
Expand Down
1 change: 1 addition & 0 deletions config/initializers/figaro.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
'twilio_auth_token',
'twilio_record_voice',
'twilio_messaging_service_sid',
'twilio_timeout',
'use_kms',
'valid_authn_contexts'
)
Expand Down
1 change: 1 addition & 0 deletions config/locales/errors/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ en:
phone_unsupported: Sorry, we are unable to send SMS at this time. Please try
the phone call option below, or use your personal key.
twilio_inbound_sms_invalid: The inbound Twilio SMS message failed validation.
twilio_timeout: The server took too long to respond. Please try again.
unauthorized_authn_context: Unauthorized authentication context
unauthorized_nameid_format: Unauthorized nameID format
unauthorized_service_provider: Unauthorized Service Provider
Expand Down
1 change: 1 addition & 0 deletions config/locales/errors/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ es:
phone_unsupported: Lo sentimos, no podemos enviar SMS en este momento. Pruebe
la opción de llamada telefónica a continuación o use su clave personal.
twilio_inbound_sms_invalid: El mensaje de Twilio SMS de entrada falló la validación.
twilio_timeout: NOT TRANSLATED YET
unauthorized_authn_context: Contexto de autenticación no autorizado
unauthorized_nameid_format: NOT TRANSLATED YET
unauthorized_service_provider: Proveedor de servicio no autorizado
Expand Down
1 change: 1 addition & 0 deletions config/locales/errors/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ fr:
le moment. S'il vous plaît essayez l'option d'appel téléphonique ci-dessous,
ou utilisez votre clé personnelle.
twilio_inbound_sms_invalid: Le message SMS Twilio entrant a échoué à la validation.
twilio_timeout: NOT TRANSLATED YET
unauthorized_authn_context: Contexte d'authentification non autorisé
unauthorized_nameid_format: NOT TRANSLATED YET
unauthorized_service_provider: Fournisseur de service non autorisé
Expand Down
39 changes: 37 additions & 2 deletions spec/services/twilio_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,15 @@
TwilioService::Utils.telephony_service = Twilio::REST::Client
end

it 'uses a real Twilio client' do
it 'uses a real Twilio client with timeout' do
allow(Figaro.env).to receive(:twilio_timeout).and_return('1')
client = instance_double(Twilio::REST::Client)
twilio_http_client = instance_double(Twilio::HTTP::Client)
expect(Twilio::HTTP::Client).to receive(:new).with(timeout: 1).and_return(twilio_http_client)
expect(Twilio::REST::Client).
to receive(:new).with(/sid(1|2)/, /token(1|2)/).and_return(client)
to receive(:new).
with(/sid(1|2)/, /token(1|2)/, nil, nil, twilio_http_client).
and_return(client)
http_client = Struct.new(:adapter)
expect(client).to receive(:http_client).and_return(http_client)
expect(http_client).to receive(:adapter=).with(:typhoeus)
Expand Down Expand Up @@ -95,6 +100,21 @@
expect { service.place_call(to: '+123456789012', url: 'https://twimlet.com') }.
to raise_error(Twilio::REST::RestError, sanitized_message)
end

it 'rescues timeout errors and raises a custom Twilio error' do
TwilioService::Utils.telephony_service = FakeVoiceCall
error_code = 4_815_162_342
status_code = 4_815_162_342

message = "[HTTP #{status_code}] #{error_code} : timeout\n\n"
service = TwilioService::Utils.new

expect(service.send(:client).calls).to receive(:create).
and_raise(Faraday::TimeoutError)

expect { service.place_call(to: '+123456789012', url: 'https://twimlet.com') }.
to raise_error(Twilio::REST::RestError, message)
end
end

describe '#send_sms' do
Expand Down Expand Up @@ -137,5 +157,20 @@
expect { service.send_sms(to: '+1 (888) 555-5555', body: 'test') }.
to raise_error(Twilio::REST::RestError, sanitized_message)
end

it 'rescues timeout errors and raises a custom Twilio error' do
TwilioService::Utils.telephony_service = FakeSms
error_code = 4_815_162_342
status_code = 4_815_162_342

message = "[HTTP #{status_code}] #{error_code} : timeout\n\n"
service = TwilioService::Utils.new

expect(service.send(:client).messages).to receive(:create).
and_raise(Faraday::TimeoutError)

expect { service.send_sms(to: '+123456789012', body: 'test') }.
to raise_error(Twilio::REST::RestError, message)
end
end
end
2 changes: 1 addition & 1 deletion spec/support/fake_sms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class FakeSms
cattr_accessor :messages
self.messages = []

def initialize(_account_sid, _auth_token); end
def initialize(_username, _password, _account_sid, _region, _http_client); end

def messages
self
Expand Down
2 changes: 1 addition & 1 deletion spec/support/fake_voice_call.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class FakeVoiceCall
cattr_accessor :calls
self.calls = []

def initialize(_account_sid, _auth_token); end
def initialize(_username, _password, _account_sid, _region, _http_client); end

def calls
self
Expand Down