Class: EVSS::DisabilityCompensationForm::SubmitForm526

Inherits:
Job
  • Object
show all
Extended by:
Logging::ThirdPartyTransaction::MethodWrapper
Defined in:
app/sidekiq/evss/disability_compensation_form/submit_form526.rb

Direct Known Subclasses

SubmitForm526AllClaim

Constant Summary collapse

RETRY =

Sidekiq has built in exponential back-off functionality for retries retry for 2d 1h 47m 12s github.com/sidekiq/sidekiq/wiki/Error-Handling

16
STATSD_KEY_PREFIX =
'worker.evss.submit_form526'

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging::ThirdPartyTransaction::MethodWrapper

wrap_with_logging

Methods inherited from Job

#submission

Methods included from Sidekiq::Form526JobStatusTracker::JobTracker

#error_message, #job_success, #job_try, #klass, #log_error, #log_info, #metrics, #update_background_job_errors, #upsert_job_status, #with_tracking

Methods included from Sidekiq::Form526JobStatusTracker::BackupSubmission

#send_backup_submission_if_enabled

Methods included from SentryLogging

#log_exception_to_sentry, #log_message_to_sentry, #non_nil_hash?, #normalize_level, #rails_logger, #set_sentry_metadata

Instance Attribute Details

#submission_idObject

Returns the value of attribute submission_id.



15
16
17
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 15

def submission_id
  @submission_id
end

Instance Method Details

#choose_service_provider(submission, service) ⇒ Object (private)

send submission data to either EVSS or Lighthouse (LH)



109
110
111
112
113
114
115
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 109

def choose_service_provider(submission, service)
  if submission.claims_api? # not needed once fully migrated to LH
    send_submission_data_to_lighthouse(submission, submission..icn)
  else
    service.submit_form526(submission.form_to_json(Form526Submission::FORM_526))
  end
end

#conditionally_handle_errors(e) ⇒ Object (private)



117
118
119
120
121
122
123
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 117

def conditionally_handle_errors(e)
  if submission.claims_api?
    handle_lighthouse_errors(submission, e)
  else
    handle_errors(submission, e)
  end
end

#fail_submission_feature_enabled?(submission) ⇒ Boolean (private)

Returns:

  • (Boolean)


129
130
131
132
133
134
135
136
137
138
139
140
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 129

def fail_submission_feature_enabled?(submission)
  if Flipper.enabled?(:disability_compensation_fail_submission,
                      OpenStruct.new({ flipper_id: submission.user_uuid }))
    with_tracking('Form526 Submission', submission.saved_claim_id, submission.id, submission.bdd?) do
      Rails.logger.info("disability_compensation_fail_submission enabled for submission #{submission.id}")
      throw StandardError
    rescue => e
      handle_errors(submission, e)
      true
    end
  end
end

#handle_errors(submission, error) ⇒ Object (private)



193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 193

def handle_errors(submission, error)
  raise error
rescue Common::Exceptions::BackendServiceException,
       Common::Exceptions::GatewayTimeout,
       Breakers::OutageException,
       EVSS::DisabilityCompensationForm::ServiceUnavailableException => e
  retryable_error_handler(submission, e)
rescue EVSS::DisabilityCompensationForm::ServiceException => e
  # retry submitting the form for specific upstream errors
  retry_form526_error_handler!(submission, e)
rescue => e
  non_retryable_error_handler(submission, e)
end

#handle_lighthouse_errors(submission, error) ⇒ Object (private)

rubocop:disable Metrics/MethodLength



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 207

def handle_lighthouse_errors(submission, error) # rubocop:disable Metrics/MethodLength
  if error.instance_of?(Common::Exceptions::UnprocessableEntity)
    error_clone = error.deep_dup
    upstream_error = error_clone.errors.first.stringify_keys
    unless (upstream_error['source'].present? && upstream_error['source']['pointer'].present?) ||
           upstream_error['detail'].downcase.include?('retries will fail')
      error = Common::Exceptions::UpstreamUnprocessableEntity.new(errors: error.errors)
    end
  end
  raise error
rescue Common::Exceptions::BackendServiceException,
       Common::Exceptions::Unauthorized, # 401 (UnauthorizedError?)
       # 422 (UpstreamUnprocessableEntity, i.e. EVSS container validation)
       Common::Exceptions::UpstreamUnprocessableEntity,
       Common::Exceptions::TooManyRequests, # 429
       Common::Exceptions::ClientDisconnected, # 499
       Common::Exceptions::ExternalServerInternalServerError, # 500
       Common::Exceptions::NotImplemented, # 501
       Common::Exceptions::BadGateway, # 502
       Common::Exceptions::ServiceUnavailable, # 503 (ServiceUnavailableException?)
       Common::Exceptions::GatewayTimeout, # 504 (already here)
       Breakers::OutageException => e
  retryable_error_handler(submission, e)
rescue EVSS::DisabilityCompensationForm::ServiceException => e
  # retry submitting the form for specific upstream errors
  retry_form526_error_handler!(submission, e)
rescue => e
  non_retryable_error_handler(submission, e)
end

#non_retryable_error_handler(submission, error) ⇒ Object (private)



243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 243

def non_retryable_error_handler(submission, error)
  # update JobStatus, log and metrics in JobStatus#non_retryable_error_handler
  super(error)
  unless Flipper.enabled?(:disability_compensation_production_tester,
                          OpenStruct.new({ flipper_id: submission.user_uuid })) ||
         Flipper.enabled?(:disability_compensation_fail_submission,
                          OpenStruct.new({ flipper_id: submission.user_uuid }))
    submission.submit_with_birls_id_that_hasnt_been_tried_yet!(
      silence_errors_and_log_to_sentry: true,
      extra_content_for_sentry: { job_class: self.class.to_s.demodulize, job_id: jid }
    )
  end
end

#perform(submission_id) ⇒ Object

Performs an asynchronous job for submitting a form526 to an upstream submission service (currently EVSS)

Parameters:



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 77

def perform(submission_id)
  Sentry.set_tags(source: '526EZ-all-claims')
  super(submission_id)

  return if fail_submission_feature_enabled?(submission)

  # This instantiates the service as defined by the inheriting object
  # TODO: this meaningless variable assignment is required for the specs to pass, which
  # indicates a problematic coupling of implementation and test logic.  This should eventually
  # be addressed to make this service and test more robust and readable.
  service = service(submission.auth_headers)

  with_tracking('Form526 Submission', submission.saved_claim_id, submission.id, submission.bdd?,
                service_provider) do
    submission.mark_birls_id_as_tried!

    return unless successfully_prepare_submission_for_evss?(submission)

    begin
      response = choose_service_provider(submission, service)
      response_handler(response)
      send_post_evss_notifications(submission, true)
    rescue => e
      send_post_evss_notifications(submission, false)
      conditionally_handle_errors(e)
    end
  end
end

#response_handler(response) ⇒ Object (private)



177
178
179
180
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 177

def response_handler(response)
  submission. = response.claim_id
  submission.save
end

#retry_form526_error_handler!(submission, error) ⇒ Object (private)

Logic for retrying a job due to an upstream service error. Retry if any upstream external service unavailability exceptions (unless it is caused by an invalid EP code) and any PIF-in-use exceptions are encountered. Otherwise the job is marked as non-retryable and completed.



273
274
275
276
277
278
279
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 273

def retry_form526_error_handler!(submission, error)
  if error.retryable?
    retryable_error_handler(submission, error)
  else
    non_retryable_error_handler(submission, error)
  end
end

#retryable_error_handler(_submission, error) ⇒ Object (private)



237
238
239
240
241
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 237

def retryable_error_handler(_submission, error)
  # update JobStatus, log and metrics in JobStatus#retryable_error_handler
  super(error)
  raise error
end

#send_post_evss_notifications(submission, send_notifications) ⇒ Object (private)



182
183
184
185
186
187
188
189
190
191
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 182

def send_post_evss_notifications(submission, send_notifications)
  actor = OpenStruct.new({ flipper_id: submission.user_uuid })
  if Flipper.enabled?(:disability_compensation_production_tester, actor)
    Rails.logger.info("send_post_evss_notifications call skipped for submission #{submission.id}")
  elsif send_notifications
    submission.send_post_evss_notifications!
  end
rescue => e
  handle_errors(submission, e)
end

#send_rrd_alert(submission, error, subtitle) ⇒ Object (private)



257
258
259
260
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 257

def send_rrd_alert(submission, error, subtitle)
  message = "RRD could not submit the claim to EVSS: #{subtitle}<br/>"
  submission.send_rrd_alert_email("RRD submission to EVSS error: #{subtitle}", message, error)
end

#send_submission_data_to_lighthouse(submission, icn) ⇒ Object (private)



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 150

def send_submission_data_to_lighthouse(submission, icn)
  # 1. transform submission data to LH format
  transform_service = EVSS::DisabilityCompensationForm::Form526ToLighthouseTransform.new
  transaction_id = submission.system_transaction_id
  body = transform_service.transform(submission.form['form526'])
  # 2. send transformed submission data to LH endpoint
  benefits_claims_service = BenefitsClaims::Service.new(icn)
  raw_response = benefits_claims_service.submit526(body, nil, nil, { transaction_id: })
  raw_response_body = if raw_response.body.is_a? String
                        JSON.parse(raw_response.body)
                      else
                        raw_response.body
                      end
  # 3. convert LH raw response to a FormSubmitResponse for further processing (claim_id, status)
  # parse claimId from LH response
   = raw_response_body.dig('data', 'attributes', 'claimId').to_i
  raw_response_struct = OpenStruct.new({
                                         body: { claim_id:  },
                                         status: raw_response.status
                                       })
  EVSS::DisabilityCompensationForm::FormSubmitResponse.new(raw_response_struct.status, raw_response_struct)
end

#service(_auth_headers) ⇒ Object (private)

Raises:

  • (NotImplementedError)


262
263
264
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 262

def service(_auth_headers)
  raise NotImplementedError, 'Subclass of SubmitForm526 must implement #service'
end

#service_providerObject (private)



125
126
127
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 125

def service_provider
  submission.claims_api? ? 'lighthouse' : 'evss'
end

#submit_complete_formObject (private)



173
174
175
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 173

def submit_complete_form
  service.submit_form526(submission.form_to_json(Form526Submission::FORM_526))
end

#successfully_prepare_submission_for_evss?(submission) ⇒ Boolean (private)

Returns:

  • (Boolean)


142
143
144
145
146
147
148
# File 'app/sidekiq/evss/disability_compensation_form/submit_form526.rb', line 142

def successfully_prepare_submission_for_evss?(submission)
  submission.prepare_for_evss!
  true
rescue => e
  handle_errors(submission, e)
  false
end