Class: V1::DecisionReviewNotificationCallbacksController

Inherits:
ApplicationController show all
Includes:
ActionController::HttpAuthentication::Token::ControllerMethods, DecisionReviewV1::Appeals::LoggingUtils
Defined in:
app/controllers/v1/decision_review_notification_callbacks_controller.rb

Constant Summary collapse

STATSD_KEY_PREFIX =
'api.decision_review.notification_callback'
DELIVERED_STATUS =
'delivered'
APPEAL_TYPE_TO_SERVICE_MAP =
{
  'HLR' => 'higher-level-review',
  'NOD' => 'board-appeal',
  'SC' => 'supplemental-claims'
}.freeze
VALID_FUNCTION_TYPES =
%w[form evidence secondary_form].freeze

Constants inherited from ApplicationController

ApplicationController::VERSION_STATUS

Constants included from SignIn::Authentication

SignIn::Authentication::BEARER_PATTERN

Constants included from ExceptionHandling

ExceptionHandling::SKIP_SENTRY_EXCEPTION_TYPES

Instance Attribute Summary

Attributes inherited from ApplicationController

#current_user

Instance Method Summary collapse

Methods included from DecisionReviewV1::Appeals::LoggingUtils

#benchmark?, #benchmark_to_log_data_hash, #extract_uuid_from_central_mail_message, #log_formatted, #parse_form412_response_to_log_msg, #parse_lighthouse_response_to_log_msg, #run_and_benchmark_if_enabled

Methods inherited from ApplicationController

#clear_saved_form, #cors_preflight, #pagination_params, #render_job_id, #routing_error, #set_csrf_header

Methods included from Traceable

#set_trace_tags

Methods included from SentryControllerLogging

#set_tags_and_extra_context, #tags_context, #user_context

Methods included from SentryLogging

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

Methods included from Instrumentation

#append_info_to_payload

Methods included from SignIn::Authentication

#access_token, #access_token_authenticate, #authenticate, #authenticate_access_token, #bearer_token, #cookie_access_token, #handle_authenticate_error, #load_user, #load_user_object, #scrub_bearer_token, #validate_request_ip

Methods included from Headers

#set_app_info_headers

Methods included from ExceptionHandling

#render_errors, #report_mapped_exception, #report_original_exception, #skip_sentry_exception?, #skip_sentry_exception_types

Methods included from AuthenticationAndSSOConcerns

#authenticate, #clear_session, #extend_session!, #load_user, #log_sso_info, #render_unauthorized, #reset_session, #set_api_cookie!, #set_current_user, #set_session_expiration_header, #set_session_object, #sign_in_service_exp_time, #sign_in_service_session, #sso_cookie_content, #sso_logging_info, #validate_inbound_login_params, #validate_session

Methods included from SignIn::AudienceValidator

#authenticate, #validate_audience!

Instance Method Details

#authenticate_headerObject (private)



80
81
82
# File 'app/controllers/v1/decision_review_notification_callbacks_controller.rb', line 80

def authenticate_header
  authenticate_user_with_token || authenticity_error
end

#authenticate_user_with_tokenObject (private)



84
85
86
87
88
89
90
91
# File 'app/controllers/v1/decision_review_notification_callbacks_controller.rb', line 84

def authenticate_user_with_token
  authenticate_with_http_token do |token|
    is_authenticated = token == bearer_token_secret
    Rails.logger.info('DecisionReviewNotificationCallbacksController callback received', is_authenticated:)

    is_authenticated
  end
end

#authenticity_errorObject (private)



93
94
95
# File 'app/controllers/v1/decision_review_notification_callbacks_controller.rb', line 93

def authenticity_error
  render json: { message: 'Invalid credentials' }, status: :unauthorized
end

#bearer_token_secretObject (private)



97
98
99
# File 'app/controllers/v1/decision_review_notification_callbacks_controller.rb', line 97

def bearer_token_secret
  Settings.nod_vanotify_status_callback.bearer_token
end

#createObject



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'app/controllers/v1/decision_review_notification_callbacks_controller.rb', line 29

def create
  return render json: nil, status: :not_found unless enabled?

  payload = JSON.parse(request.body.string)
  status = payload['status']&.downcase
  reference = payload['reference']

  StatsD.increment("#{STATSD_KEY_PREFIX}.received", tags: { status: })
  send_silent_failure_avoided_metric(reference) if status == DELIVERED_STATUS

  DecisionReviewNotificationAuditLog.create!(notification_id: payload['id'], reference:, status:, payload:)

  log_formatted(**log_params(payload, true))
  render json: { message: 'success' }
rescue => e
  log_formatted(**log_params(payload, false), params: { exception_message: e.message })
  render json: { message: 'failed' }
end

#enabled?Boolean (private)

Returns:

  • (Boolean)


101
102
103
# File 'app/controllers/v1/decision_review_notification_callbacks_controller.rb', line 101

def enabled?
  Flipper.enabled? :nod_callbacks_endpoint
end

#log_params(payload, is_success) ⇒ Object (private)



50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'app/controllers/v1/decision_review_notification_callbacks_controller.rb', line 50

def log_params(payload, is_success)
  {
    key: :decision_review_notification_callback,
    form_id: '995',
    user_uuid: nil,
    upstream_system: 'VANotify',
    body: payload.merge('to' => '<FILTERED>'), # scrub PII from logs
    is_success:,
    params: {
      notification_id: payload['id'],
      status: payload['status']
    }
  }
end

#parse_reference_value(reference) ⇒ Object (private)



73
74
75
76
77
78
# File 'app/controllers/v1/decision_review_notification_callbacks_controller.rb', line 73

def parse_reference_value(reference)
  appeal_type, function_type = reference.split('-')
  raise 'Invalid function_type' unless VALID_FUNCTION_TYPES.include? function_type

  [APPEAL_TYPE_TO_SERVICE_MAP.fetch(appeal_type.upcase), function_type]
end

#send_silent_failure_avoided_metric(reference) ⇒ Object (private)



65
66
67
68
69
70
71
# File 'app/controllers/v1/decision_review_notification_callbacks_controller.rb', line 65

def send_silent_failure_avoided_metric(reference)
  service_name, function_type = parse_reference_value(reference)
  tags = ["service:#{service_name}", "function: #{function_type} submission to Lighthouse"]
  StatsD.increment('silent_failure_avoided', tags:)
rescue => e
  Rails.logger.error('Failed to send silent_failure_avoided metric', params: { reference:, message: e.message })
end