-
Notifications
You must be signed in to change notification settings - Fork 167
LG-8738: Handle unexpected API responses when checking enrollment status #7743
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0961024
9beed67
1b857ac
877d054
a307a61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,9 @@ class GetUspsProofingResultsJob < ApplicationJob | |
| IPP_STATUS_PASSED = 'In-person passed' | ||
| IPP_STATUS_FAILED = 'In-person failed' | ||
| IPP_INCOMPLETE_ERROR_MESSAGE = 'Customer has not been to a post office to complete IPP' | ||
| IPP_EXPIRED_ERROR_MESSAGE = 'More than 30 days have passed since opt-in to IPP' | ||
| IPP_EXPIRED_ERROR_MESSAGE = /More than (?<days>\d+) days have passed since opt-in to IPP/ | ||
| IPP_INVALID_ENROLLMENT_CODE_MESSAGE = 'Enrollment code %s does not exist' | ||
| IPP_INVALID_APPLICANT_MESSAGE = 'Applicant %s does not exist' | ||
| SUPPORTED_ID_TYPES = [ | ||
| "State driver's license", | ||
| "State non-driver's identification card", | ||
|
|
@@ -138,18 +140,25 @@ def analytics(user: AnonymousUser.new) | |
| end | ||
|
|
||
| def handle_bad_request_error(err, enrollment) | ||
| response = err.response_body | ||
| case response&.[]('responseMessage') | ||
| when IPP_INCOMPLETE_ERROR_MESSAGE | ||
| response_body = err.response_body | ||
| response_message = response_body&.[]('responseMessage') | ||
|
|
||
| if response_message == IPP_INCOMPLETE_ERROR_MESSAGE | ||
| # Customer has not been to post office for IPP | ||
| enrollment_outcomes[:enrollments_in_progress] += 1 | ||
| when IPP_EXPIRED_ERROR_MESSAGE | ||
| handle_expired_status_update(enrollment, err.response) | ||
| elsif response_message&.match(IPP_EXPIRED_ERROR_MESSAGE) | ||
| handle_expired_status_update(enrollment, err.response, response_message) | ||
| elsif response_message == IPP_INVALID_ENROLLMENT_CODE_MESSAGE % enrollment.enrollment_code | ||
| handle_unexpected_response(enrollment, response_message, reason: 'Invalid enrollment code') | ||
| elsif response_message == IPP_INVALID_APPLICANT_MESSAGE % enrollment.unique_id | ||
| handle_unexpected_response( | ||
| enrollment, response_message, reason: 'Invalid applicant unique id' | ||
| ) | ||
| else | ||
| NewRelic::Agent.notice_error(err) | ||
| analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_exception( | ||
| **enrollment_analytics_attributes(enrollment, complete: false), | ||
| **response_analytics_attributes(response), | ||
| **response_analytics_attributes(response_body), | ||
| exception_class: err.class.to_s, | ||
| exception_message: err.message, | ||
| reason: 'Request exception', | ||
|
|
@@ -231,7 +240,7 @@ def handle_unsupported_id_type(enrollment, response) | |
| enrollment.update(status: :failed) | ||
| end | ||
|
|
||
| def handle_expired_status_update(enrollment, response) | ||
| def handle_expired_status_update(enrollment, response, response_message) | ||
| enrollment_outcomes[:enrollments_expired] += 1 | ||
| analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_enrollment_updated( | ||
| **enrollment_analytics_attributes(enrollment, complete: true), | ||
|
|
@@ -259,6 +268,31 @@ def handle_expired_status_update(enrollment, response) | |
| ) | ||
| enrollment.update(deadline_passed_sent: true) | ||
| end | ||
|
|
||
| # check for an unexpected number of days until expiration | ||
| match = response_message&.match(IPP_EXPIRED_ERROR_MESSAGE) | ||
| expired_after_days = match && match[:days] | ||
| if expired_after_days.present? && | ||
| expired_after_days.to_i != IdentityConfig.store.in_person_enrollment_validity_in_days | ||
| handle_unexpected_response( | ||
| enrollment, | ||
| response_message, | ||
| reason: 'Unexpected number of days before enrollment expired', | ||
| cancel: false, | ||
| ) | ||
| end | ||
| end | ||
|
|
||
| def handle_unexpected_response(enrollment, response_message, reason:, cancel: true) | ||
| enrollment.cancelled! if cancel | ||
|
|
||
| analytics(user: enrollment.user). | ||
| idv_in_person_usps_proofing_results_job_unexpected_response( | ||
| enrollment_code: enrollment.enrollment_code, | ||
| enrollment_id: enrollment.id, | ||
| response_message: response_message, | ||
| reason: reason, | ||
| ) | ||
| end | ||
|
Comment on lines
+286
to
296
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have a specific plan for how we can be notified of these issues and respond?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, not yet. I'm going to confirm that they work in lower environments, then I'll probably write up a ticket for alerting. |
||
|
|
||
| def handle_failed_status(enrollment, response) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "responseMessage": "More than 4 days have passed since opt-in to IPP" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "responseMessage": "Applicant 123456789abcdefghi does not exist" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "responseMessage": "Enrollment code 1234567890123456 does not exist" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -561,6 +561,81 @@ | |
| end | ||
| end | ||
|
|
||
| context 'when an enrollment expires unexpectedly' do | ||
| before(:each) do | ||
| stub_request_unexpected_expired_proofing_results | ||
| end | ||
|
|
||
| it_behaves_like( | ||
| 'enrollment_with_a_status_update', | ||
| passed: false, | ||
| status: 'expired', | ||
| response_json: UspsInPersonProofing::Mock::Fixtures. | ||
| request_unexpected_expired_proofing_results_response, | ||
| ) | ||
|
|
||
| it 'logs that the enrollment expired unexpectedly' do | ||
| allow(IdentityConfig.store).to( | ||
| receive(:in_person_enrollment_validity_in_days).and_return(30), | ||
| ) | ||
| job.perform(Time.zone.now) | ||
|
|
||
| expect(job_analytics).to have_logged_event( | ||
| 'GetUspsProofingResultsJob: Enrollment status updated', | ||
| hash_including(reason: 'Enrollment has expired'), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| ) | ||
|
|
||
| expect(job_analytics).to have_logged_event( | ||
| 'GetUspsProofingResultsJob: Unexpected response received', | ||
| hash_including(reason: 'Unexpected number of days before enrollment expired'), | ||
| ) | ||
| end | ||
| end | ||
|
|
||
| context 'when an enrollment is reported as invalid' do | ||
| context 'when an enrollment code is invalid' do | ||
| # this enrollment code is hardcoded into the fixture | ||
| # request_unexpected_invalid_enrollment_code_response.json | ||
| let(:pending_enrollment) do | ||
| create(:in_person_enrollment, :pending, enrollment_code: '1234567890123456') | ||
| end | ||
| before(:each) do | ||
| stub_request_unexpected_invalid_enrollment_code | ||
| end | ||
|
|
||
| it 'cancels the enrollment and logs that it was invalid' do | ||
| job.perform(Time.zone.now) | ||
|
|
||
| expect(pending_enrollment.reload.cancelled?).to be_truthy | ||
| expect(job_analytics).to have_logged_event( | ||
| 'GetUspsProofingResultsJob: Unexpected response received', | ||
| hash_including(reason: 'Invalid enrollment code'), | ||
| ) | ||
| end | ||
| end | ||
|
|
||
| context 'when a unique id is invalid' do | ||
| # this unique id is hardcoded into the fixture | ||
| # request_unexpected_invalid_applicant_response.json | ||
| let(:pending_enrollment) do | ||
| create(:in_person_enrollment, :pending, unique_id: '123456789abcdefghi') | ||
| end | ||
| before(:each) do | ||
| stub_request_unexpected_invalid_applicant | ||
| end | ||
|
|
||
| it 'cancels the enrollment and logs that it was invalid' do | ||
| job.perform(Time.zone.now) | ||
|
|
||
| expect(pending_enrollment.reload.cancelled?).to be_truthy | ||
| expect(job_analytics).to have_logged_event( | ||
| 'GetUspsProofingResultsJob: Unexpected response received', | ||
| hash_including(reason: 'Invalid applicant unique id'), | ||
| ) | ||
| end | ||
| end | ||
| end | ||
|
|
||
| context 'when USPS returns a non-hash response' do | ||
| before(:each) do | ||
| stub_request_proofing_results_with_responses({}) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TY for this helpful comment. 🙏🏻