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
2 changes: 1 addition & 1 deletion app/views/shared/_troubleshooting_options.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
locals:
* heading_tag: Tag name for heading. Does not affect visual appearance. Defaults to :h2.
* heading: Heading text.
* options: List of link options to display, as an array of hashes with `url`, `text`, `external` values.
* options: List of link options to display, as an array of hashes with `url`, `text`, `new_tab` values.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops 😅 Thanks for fixing.

Interestingly, for reasons I cannot recall, we call it both things in the JSX implementation 🤔

{options.map(({ url, text, isExternal }) => (
<li key={url}>
<BlockLink url={url} isNewTab={isExternal}>

* class: Additional class names to add to wrapper element.
%>
<% if local_assigns[:options].presence %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
{
url: MarketingSite.contact_url,
text: t('links.contact_support', app_name: APP_NAME),
new_tab: true,
},
].select(&:present?),
) %>
Expand Down
6 changes: 5 additions & 1 deletion lib/telephony/pinpoint/sms_sender.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ def send(message:, to:, country_code:, otp: nil)
)
finish = Time.zone.now
response = build_response(pinpoint_response, start: start, finish: finish)
return response if response.success?
if response.success? ||
response.error.is_a?(OptOutError) ||
response.error.is_a?(PermanentFailureError)
Comment on lines +49 to +50
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider the SMS opt-in configuration in deciding to fail fast or allow failover?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean IdentityConfig.store.allow_sms_resubscribe? Or something else?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean IdentityConfig.store.allow_sms_resubscribe? Or something else?

Yes. In other words, should this logic only apply if we're allowing SMS resubscribe in the environment?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking is no, this logic should apply always, opt out means "don't send me SMS" so I think we don't want to failover or retry if we get that response.

We have older opt-out detecting logic that shows a flash error "you have opted out" (that isn't actionable), so this would result in that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense 👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For considering PermanetnFailureError, should we limit it to the cases like what we're matching with this logic?

# Sometimes AWS Pinpoint returns PERMANENT_FAILURE with an "opted out" message
# instead of an OPT_OUT error
# @param [Aws::Pinpoint::Types::MessageResult] message_response_result
def permanent_failure_opt_out?(message_response_result)
message_response_result.delivery_status == 'PERMANENT_FAILURE' &&
message_response_result.status_message&.include?('opted out')
end

On the other hand, if it is a permanent failure, regardless the specific, maybe we don't want failover anyways.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So... that logic you linked turns PERMANENT_FAILURE into OptOutError classes (because it seemed to be logically what it was representing)

I figure other permanent failures might include numbers that don't exist, or something so they seemed worth terminating on as well:

https://docs.aws.amazon.com/pinpoint/latest/apireference/apps-application-id-messages.html

return response
end
PinpointHelper.notify_pinpoint_failover(
error: response.error,
region: sms_config.region,
Expand Down
40 changes: 39 additions & 1 deletion spec/lib/telephony/pinpoint/sms_sender_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def ==(other)
end
end

context 'when the first config errors' do
context 'when the first config errors with a transient error' do
before do
Pinpoint::MockClient.message_response_result_status_code = 400
Pinpoint::MockClient.message_response_result_delivery_status = 'DUPLICATE'
Expand All @@ -271,6 +271,44 @@ def ==(other)
end
end

context 'when the first config errors with an opt out error' do
before do
Pinpoint::MockClient.message_response_result_status_code = 400
Pinpoint::MockClient.message_response_result_delivery_status = 'OPT_OUT'
end

it 'only tries one region and returns an error' do
expect(backup_mock_client).to_not receive(:send_messages)

response = subject.send(
message: 'This is a test!',
to: '+1 (123) 456-7890',
country_code: 'US',
)
expect(response.success?).to eq(false)
expect(response.error).to be_present
end
end

context 'when the first config errors with a permanent error' do
before do
Pinpoint::MockClient.message_response_result_status_code = 400
Pinpoint::MockClient.message_response_result_delivery_status = 'PERMANENT_FAILURE'
end

it 'only tries one region and returns an error' do
expect(backup_mock_client).to_not receive(:send_messages)

response = subject.send(
message: 'This is a test!',
to: '+1 (123) 456-7890',
country_code: 'US',
)
expect(response.success?).to eq(false)
expect(response.error).to be_present
end
end

context 'when the first config raises a timeout exception' do
let(:raised_error_message) { 'Seahorse::Client::NetworkingError: Net::ReadTimeout' }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@
end

context 'troubleshooting links' do
it 'links to the contact form in a new window' do
render

expect(rendered).to have_link(
t('links.contact_support', app_name: APP_NAME),
href: MarketingSite.contact_url,
class: 'usa-link--external',
)
end

context 'without an other_mfa_options_url' do
let(:other_mfa_options_url) { nil }

Expand Down