Skip to content
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

Aces mec check call pattern change #52

Merged
merged 6 commits into from
Oct 22, 2021
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ coverage

event_source.*

.vscode
# .vscode

# Make sure no PII is accidently commited
# add w/ git add --force if actually docs
Expand Down
9 changes: 7 additions & 2 deletions app/event_source/subscribers/mec_check_subscriber.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ module Subscribers
class MecCheckSubscriber
include ::EventSource::Subscriber[amqp: 'enroll.iap.mec_check']

subscribe(:on_enroll_iap_mec_check) do |delivery_info, _metadata, response|
result = Aces::InitiateMecCheck.new.call(response)
subscribe(:on_enroll_iap_mec_check) do |delivery_info, metadata, response|
payload_type = metadata[:headers]["payload_type"]
result = if payload_type == "person"
Aces::InitiateMecCheck.new.call(response)
else
Aces::InitiateMecChecks.new.call(response)
end
if result.success?
ack(delivery_info.delivery_tag)
logger.debug "application_submitted_subscriber_message; acked"
Expand Down
2 changes: 1 addition & 1 deletion app/event_source/subscribers/transfer_subscriber.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class TransferSubscriber
Aces::Transfer.new.call(
{
application_identifier: response.to_s,
family_identifier: result,
family_identifier: result.to_s,
service: "subscriber failure",
failure: e
}
Expand Down
76 changes: 76 additions & 0 deletions app/operations/aces/applicant_mec_check_call.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# frozen_string_literal: true

require 'dry/monads'
require 'dry/monads/do'
require 'aca_entities/medicaid/mec_check/operations/generate_xml'
require 'aca_entities/operations/encryption/decrypt'

module Aces
# Check for the existance of an applicant in the Medicare system already, and if so did they have coverage.
class ApplicantMecCheckCall
send(:include, Dry::Monads[:result, :do])

# @param [String] hbxid of application
# @return [Dry::Result]
def call(person_payload)
ssn = yield decrypt_ssn(person_payload)
person_hash = yield map_to_person(person_payload, ssn)
xml = yield generate_xml(person_hash)
_validate_xml = yield validate_xml(xml)
built_check = yield build_check_request(xml)
encoded_check = yield encode_check(built_check)
submit_check(encoded_check)
end

protected

def decrypt_ssn(payload)
ssn = payload["identifying_information"]["encrypted_ssn"]
AcaEntities::Operations::Encryption::Decrypt.new.call({ value: ssn })
end

def map_to_person(payload, ssn)
payload["person"] = {}
payload["person"]["person"] = {}
payload["person"]["person"]["person_demographics"] = {}
payload["person"]["person"]["person_name"] = payload["name"]
payload["person"]["person"]["person_demographics"]["ssn"] = ssn
payload["person"]["person"]["person_demographics"]["dob"] = payload["demographic"]["dob"]
payload["person"]["person"]["person_demographics"]["gender"] = payload["demographic"]["gender"]
Success(payload)
end

def generate_xml(payload)
transfer_request = ::AcaEntities::Medicaid::MecCheck::Operations::GenerateXml.new.call(payload.to_json)
transfer_request.success? ? transfer_request : Failure("Generate XML failure: #{transfer_request.failure}")
end

def validate_xml(seralized_xml)
document = Nokogiri::XML(seralized_xml)
xsd_path = File.open(Pathname.pwd.join("spec/test_data/nonesi_mec.xsd"))
schema_location = File.expand_path(xsd_path)
schema = Nokogiri::XML::Schema(File.open(schema_location))
result = schema.validate(document).each_with_object([]) do |error, collect|
collect << error.message
end

if result.empty?
Success(true)
else
Failure("validate_xml -> #{result}")
end
end

def build_check_request(transfer)
Aces::BuildAccountTransferRequest.new.call(transfer)
end

def encode_check(payload)
Aces::EncodeAccountTransferRequest.new.call(payload)
end

def submit_check(encoded_check)
Aces::SubmitMecCheckPayload.new.call(encoded_check)
end
end
end
30 changes: 3 additions & 27 deletions app/operations/aces/initiate_mec_check.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@
require 'dry/monads/do'

module Aces
# Take a payload and find people. For each person, initiate a MecCheck
# Take a payload and for each person, initiate a MEC Check
class InitiateMecCheck
send(:include, Dry::Monads[:result, :do])

# @param [String] payload received from Enroll
# @return [Dry::Result]
def call(payload)
return Failure({ error: "MEC Check feature not enabled." }) unless MedicaidGatewayRegistry[:mec_check].enabled?

json = JSON.parse(payload)
mec_check = yield create_mec_check(json)
checks = yield get_person_check(mec_check, json) if json["person"]
checks = yield get_people_checks(mec_check, json) if json["people"]
checks = yield get_person_check(mec_check, json)
results = yield update_mec_check(mec_check, checks)

publish_to_enroll(mec_check, results)
Expand Down Expand Up @@ -56,35 +54,13 @@ def get_person_check(mec_check, json)
Success(results)
end

def get_people_checks(mec_check, json)
results = {}
people = json["people"]
people.each do |person|
person_hash = { "person" => {} }
person_hash["person"]["person"] = person
mc_response = mec_check(person_hash)

if mc_response.failure?
error_result = {
mc_id: mec_check.id,
error: "#{mc_response.failure} on Person: #{person['hbx_id']}"
}
return Failure(error_result)
end

results[person["hbx_id"]] = mc_response.value!
end
Success(results)
end

def mec_check(person)
result = call_mec_check(person)
return Failure(result.failure) if result.failure?

response = JSON.parse(result.value!.to_json)
xml = Nokogiri::XML(response["body"])
response_description = xml.xpath("//xmlns:ResponseDescription", "xmlns" => "http://gov.hhs.cms.hix.dsh.ee.nonesi_mec.ext")

response_description.empty? ? Failure("XML error: ResponseDescription tag missing.") : Success(response_description.text)
end

Expand All @@ -103,7 +79,7 @@ def update_mec_check(mec_check, checks)
end

def publish_to_enroll(mec_check, payload)
transfer = Aces::InitiateMecCheckToEnroll.new.call(payload.attributes)
transfer = Aces::InitiateMecCheckToEnroll.new.call(payload.attributes, "person")
if transfer.success?
Success("Transferred MEC Check to Enroll")
else
Expand Down
8 changes: 4 additions & 4 deletions app/operations/aces/initiate_mec_check_to_enroll.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ class InitiateMecCheckToEnroll

# @option opts [Hash] :mec_check
# @return [Dry::Monads::Result]
def call(params)
event = yield build_event(params)
def call(params, payload_type)
event = yield build_event(params, payload_type)
result = send_to_enroll(event)

Success(result)
end

private

def build_event(params)
result = event("events.magi_medicaid.mec_check.mec_checked", attributes: params)
def build_event(params, payload_type)
result = event("events.magi_medicaid.mec_check.mec_checked", attributes: params, headers: { payload_type: payload_type })
logger.info "MedicaidGateway MEC Check Publisher to enroll, event_key: mec_checked, attributes: #{params.to_h}, result: #{result}"
logger.info('-' * 100)
result
Expand Down
113 changes: 113 additions & 0 deletions app/operations/aces/initiate_mec_checks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# frozen_string_literal: true

require 'dry/monads'
require 'dry/monads/do'
require 'aca_entities/medicaid/mec_check'

module Aces
# Take an application payload and add mec check results as evidence to applicants
class InitiateMecChecks
send(:include, Dry::Monads[:result, :do])

# @param [String] payload received from Enroll
# @return [Dry::Result]
def call(payload)
return Failure({ error: "MEC Check feature not enabled." }) unless MedicaidGatewayRegistry[:mec_check].enabled?
json = JSON.parse(payload)
mec_check = yield create_mec_check(json)
checks = yield get_applicant_checks(mec_check, json)
_results = yield update_mec_check(mec_check, checks)

publish_to_enroll(mec_check, checks)
end

protected

def create_mec_check(json)
application = json["hbx_id"] || "n/a"
family = json["family_reference"]["hbx_id"] || "n/a"
results = Aces::CreateMecCheck.new.call(
{
application_identifier: application,
family_identifier: family,
type: "application"
}
)

return results if results.success?
Failure({ error: "Failed to save MEC check: #{results.failure}" })
end

def get_applicant_checks(mec_check, json)
results = {}
people = json["applicants"]
people.each do |person|
hbx_id = person["person_hbx_id"]
evidence = person["evidences"].detect { |evi| evi["key"] == "aces_mec" }
if evidence
mc_response = mec_check(person)
if mc_response.failure?
error_result = {
mc_id: mec_check.id,
error: "#{mc_response.failure} on Person}"
}
return Failure(error_result)
end
response = mc_response.value!
results[hbx_id] = response[:code_description]
evidence["eligibility_results"] = [response]
evidence["eligibility_status"] = response[:mec_verification_code] == "Y" ? :outstanding : :attested
else
results[hbx_id] = "not MEC checked"
end
end
Success([json, results])
rescue StandardError => e
Failure("Mec check failure => #{e}")
end

def mec_check(person)
result = call_mec_check(person)
return Failure(result.failure) if result.failure?
response = JSON.parse(result.value!.to_json)
serialize_response(response["body"])
end

def serialize_response(response)
xml = Nokogiri::XML(response)
body = xml.at_xpath("//xml_ns:VerifyNonESIMECResponse", "xml_ns" => "http://gov.hhs.cms.hix.dsh.ee.nonesi_mec.ext")
Success(AcaEntities::Medicaid::MecCheck::Operations::GenerateResult.new.call(body.to_xml))
rescue StandardError => e
p e.backtrace
Failure("Serializing response error => #{e}")
end

def call_mec_check(person)
Aces::ApplicantMecCheckCall.new.call(person)
end

def update_mec_check(mec_check, checks)
return Success(mec_check) if mec_check.update(applicant_responses: checks[1])

error_result = {
mc_id: mec_check.id,
error: "Failed to save MEC check response: #{mec_check.errors.to_h}"
}
Failure(error_result)
end

def publish_to_enroll(mec_check, results)
payload = results[0]
transfer = Aces::InitiateMecCheckToEnroll.new.call(payload, "application")
if transfer.success?
Success("Transferred MEC Check to Enroll")
else
error_result = {
mc_id: mec_check.id,
error: "Failed to transfer MEC to enroll: #{transfer.failure}"
}
Failure(error_result)
end
end
end
end
12 changes: 10 additions & 2 deletions app/operations/aces/mec_check_call.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'dry/monads'
require 'dry/monads/do'
require 'aca_entities/medicaid/mec_check/operations/generate_xml'
require 'aca_entities/operations/encryption/decrypt'

module Aces
# Check for the existance of a person in the Medicare system already, and if so did they have coverage.
Expand All @@ -12,7 +13,8 @@ class MecCheckCall
# @param [String] hbxid of application
# @return [Dry::Result]
def call(person_payload)
xml = yield generate_xml(person_payload)
ssn = yield decrypt_ssn(person_payload)
xml = yield generate_xml(person_payload, ssn)
_validate_xml = yield validate_xml(xml)
built_check = yield build_check_request(xml)
encoded_check = yield encode_check(built_check)
Expand All @@ -21,7 +23,13 @@ def call(person_payload)

protected

def generate_xml(payload)
def decrypt_ssn(payload)
ssn = payload["person"][:person]["person_demographics"]["encrypted_ssn"]
AcaEntities::Operations::Encryption::Decrypt.new.call({ value: ssn })
end

def generate_xml(payload, ssn)
payload["person"][:person]["person_demographics"]["ssn"] = ssn
transfer_request = ::AcaEntities::Medicaid::MecCheck::Operations::GenerateXml.new.call(payload.to_json)
transfer_request.success? ? transfer_request : Failure("Generate XML failure: #{transfer_request.failure}")
end
Expand Down
8 changes: 4 additions & 4 deletions app/operations/transfers/to_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def initiate_transfer(payload, transfer_id)
end

if result.success?
update_transfer(transfer_id, result.value!)
update_transfer(transfer_id, result.value!, payload)
Success("Successfully transferred in account")
else
error_result = {
Expand All @@ -84,16 +84,16 @@ def initiate_transfer(payload, transfer_id)
end
end

def update_transfer(transfer_id, response)
def update_transfer(transfer_id, response, payload)
transfer = Aces::Transfer.find(transfer_id)
response_json = response.to_json
if @service == "aces"
xml = Nokogiri::XML(response.to_hash[:body])
status = xml.xpath('//tns:ResponseDescriptionText', 'tns' => 'http://hix.cms.gov/0.1/hix-core')
status_text = status.any? ? status.last.text : "N/A"
transfer.update!(response_payload: response_json, callback_status: status_text)
transfer.update!(response_payload: response_json, callback_status: status_text, payload: payload)
else
transfer.update!(response_payload: response_json)
transfer.update!(response_payload: response_json, payload: payload)
end
end

Expand Down
6 changes: 6 additions & 0 deletions config/initializers/aca_entities.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

AcaEntities::Configuration::Encryption.configure do |config|
config.secret_key = ENV['RBNACL_SECRET_KEY'] || "C639A572E14D5075C526FDDD43E4ECF6B095EA17783D32EF3D2710AF9F359DD4"
config.iv = ENV['RBNACL_IV'] || "1234567890ABCDEFGHIJKLMN"
end
Loading