-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
creates operational process for force syncing for crm (#4156)
- Loading branch information
Showing
5 changed files
with
364 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
# frozen_string_literal: true | ||
|
||
module Operations | ||
module Crm | ||
module Family | ||
# This operation is responsible for publishing an event for a a primary person. | ||
class Publish | ||
include Dry::Monads[:do, :result] | ||
include EventSource::Command | ||
|
||
# Publishes an event for a given person identified by hbx_id. | ||
# | ||
# @param hbx_id [String] The HBX ID of the person. | ||
# @return [Dry::Monads::Result] The result of the publish operation. | ||
def call(hbx_id:) | ||
person = yield find_person(hbx_id) | ||
family = yield find_primary_family(person) | ||
headers = yield headers(family, person) | ||
transformed_cv = yield construct_cv_transform(family, hbx_id) | ||
family_entity = yield create_family_entity(transformed_cv) | ||
es_event = yield create_es_event(family_entity, headers) | ||
published_result = yield publish_es_event(es_event, hbx_id) | ||
|
||
Success(published_result) | ||
end | ||
|
||
private | ||
|
||
# Finds a person by their HBX ID. | ||
# | ||
# @param hbx_id [String] The HBX ID of the person. | ||
# @return [Dry::Monads::Result] The result containing the person or an error message. | ||
def find_person(hbx_id) | ||
result = ::Operations::People::Find.new.call({ person_hbx_id: hbx_id }) | ||
|
||
if result.success? | ||
Success(result.value!) | ||
else | ||
Failure("Provide a valid person_hbx_id to fetch person. Invalid input hbx_id: #{hbx_id}") | ||
end | ||
end | ||
|
||
# Finds the primary family of a given person. | ||
# | ||
# @param person [Person] The person object. | ||
# @return [Dry::Monads::Result] The result containing the family or an error message. | ||
def find_primary_family(person) | ||
family = person.primary_family | ||
|
||
if family | ||
Success(family) | ||
else | ||
Failure("Primary Family does not exist with given hbx_id: #{person.hbx_id}") | ||
end | ||
end | ||
|
||
# Constructs headers for the event. | ||
# | ||
# @param family [Family] The family object. | ||
# @param person [Person] The person object. | ||
# @return [Dry::Monads::Result] The result containing the headers. | ||
def headers(family, person) | ||
eligible_dates = [person.created_at, person.updated_at, family.created_at, family.updated_at].compact | ||
|
||
Success({ after_updated_at: eligible_dates.max, before_updated_at: eligible_dates.min }) | ||
end | ||
|
||
# Constructs a CV transform for the family. | ||
# | ||
# @param family [Family] The family object. | ||
# @param hbx_id [String] The HBX ID of the person. | ||
# @return [Dry::Monads::Result] The result containing the transformed CV or an error message. | ||
def construct_cv_transform(family, hbx_id) | ||
::Operations::Transformers::FamilyTo::Cv3Family.new.call(family) | ||
rescue StandardError => e | ||
Failure("Failed to transform family with primary person_hbx_id: #{hbx_id} to CV: #{e.message}") | ||
end | ||
|
||
# Creates a family entity from the transformed CV. | ||
# | ||
# @param transformed_cv [Hash] The transformed CV. | ||
# @return [Dry::Monads::Result] The result containing the family entity. | ||
def create_family_entity(transformed_cv) | ||
::AcaEntities::Operations::CreateFamily.new.call(transformed_cv) | ||
end | ||
|
||
# Creates an event source event for the family entity. | ||
# | ||
# @param family_entity [FamilyEntity] The family entity object. | ||
# @param headers [Hash] The headers for the event. | ||
# @return [Dry::Monads::Result] The result containing the event. | ||
def create_es_event(family_entity, headers) | ||
event( | ||
'events.families.created_or_updated', | ||
attributes: { before_save_cv_family: {}, after_save_cv_family: family_entity.to_h }, | ||
headers: headers | ||
) | ||
end | ||
|
||
# Publishes the event source event. | ||
# | ||
# @param es_event [EventSource::Event] The event source event. | ||
# @param hbx_id [String] The HBX ID of the person. | ||
# @return [Dry::Monads::Result] The result of the publish operation. | ||
def publish_es_event(es_event, hbx_id) | ||
es_event.publish | ||
Success("Successfully published event: #{es_event.name} for family with primary person hbx_id: #{hbx_id}") | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# frozen_string_literal: true | ||
|
||
module Operations | ||
module Crm | ||
# This operation is responsible for initiating the force sync operation. | ||
class ForceSync | ||
include Dry::Monads[:do, :result] | ||
include EventSource::Command | ||
|
||
# Initiates the force sync operation. | ||
# | ||
# @param params [Hash] the parameters for the operation | ||
# @option params [Array<String>] :primary_hbx_ids an array of primary HBX IDs | ||
# @return [Dry::Monads::Result] the result of the operation | ||
def call(params) | ||
primary_person_hbx_ids = yield validate(params) | ||
csv_file_name = yield publish_families(primary_person_hbx_ids) | ||
message = yield generate_success_message(csv_file_name) | ||
|
||
Success(message) | ||
end | ||
|
||
private | ||
|
||
# Validates the input parameters. | ||
# | ||
# @param params [Hash] the parameters to validate | ||
# @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure] the validation result | ||
def validate(params) | ||
if params[:primary_hbx_ids].is_a?(Array) && params[:primary_hbx_ids].present? && params[:primary_hbx_ids].all? { |id| id.is_a?(String) } | ||
Success(params[:primary_hbx_ids]) | ||
else | ||
Failure("Invalid input for primary_hbx_ids: #{params[:primary_hbx_ids]}. Provide an array of HBX IDs.") | ||
end | ||
end | ||
|
||
# Publishes each family and logs o/p into a CSV file. | ||
# | ||
# @param primary_person_hbx_ids [Array<String>] an array of primary HBX IDs | ||
# @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure] the result of the publishing operation | ||
def publish_families(primary_person_hbx_ids) | ||
csv_file_name = "#{Rails.root}/crm_force_sync_#{DateTime.now.strftime('%Y_%m_%d_%H_%M_%S')}.csv" | ||
|
||
CSV.open(csv_file_name, 'w', force_quotes: true) do |csv| | ||
csv << ['Hbx ID', 'Result', 'Message'] | ||
|
||
primary_person_hbx_ids.each do |hbx_id| | ||
result = ::Operations::Crm::Family::Publish.new.call(hbx_id: hbx_id) | ||
|
||
csv << [ | ||
hbx_id, | ||
result.success? ? 'Success' : 'Failed', | ||
result.success? ? result.success : result.failure | ||
] | ||
end | ||
end | ||
|
||
Success(csv_file_name) | ||
end | ||
|
||
def generate_success_message(csv_file_name) | ||
Success("Successfully published events for all families. Review the CSV file with results: #{csv_file_name}") | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# frozen_string_literal: true | ||
|
||
# This script takes a string of comma separated hbx_ids and initiates the force sync operation. | ||
|
||
# Command to trigger the script: | ||
# CLIENT=me bundle exec rails runner script/crm/trigger_force_sync.rb 'hbx_id1, hbx_id2, hbx_id3' | ||
|
||
# Checks if the :async_publish_updated_families feature is enabled in the EnrollRegistry. | ||
# If the feature is not enabled, it prints a message and exits the script. | ||
# | ||
# @return [void] | ||
unless EnrollRegistry.feature_enabled?(:async_publish_updated_families) | ||
puts 'The CRM Sync Refactor feature is not enabled. Please enable the feature :async_publish_updated_families to run this script. EXITING.' | ||
exit | ||
end | ||
|
||
# Creates an array of hbx_ids from the input string and removes any nil values, spaces or empty strings. | ||
# @example | ||
# bundle exec rails runner script/crm/trigger_force_sync.rb 'hbx_id1, hbx_id2, hbx_id3' | ||
# @param [String] ARGV[0] a comma separated list of HBX IDs | ||
# @return [void] | ||
primary_person_hbx_ids = begin | ||
# The below line splits the input string by comma, removes any leading or trailing spaces, converts the values to string, and rejects any empty strings. | ||
ARGV[0].split(',').map { |hbx_id| hbx_id.strip.to_s }.reject(&:empty?) | ||
rescue StandardError => e | ||
puts "Error: #{e.message}. Invalid input for primary_hbx_ids: #{ARGV[0]}. Provide a comma separated list of HBX IDs." | ||
exit | ||
end | ||
|
||
# Initiates the force sync operation with the provided HBX IDs. | ||
# @param [Array<String>] primary_hbx_ids an array of HBX IDs | ||
# @return [void] | ||
result = ::Operations::Crm::ForceSync.new.call({ primary_hbx_ids: primary_person_hbx_ids }) | ||
if result.success? | ||
puts result.success | ||
else | ||
puts result.failure | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rails_helper' | ||
|
||
RSpec.describe Operations::Crm::Family::Publish do | ||
before :all do | ||
DatabaseCleaner.clean | ||
end | ||
|
||
after :all do | ||
DatabaseCleaner.clean | ||
end | ||
|
||
let(:primary) { FactoryBot.create(:person, :with_consumer_role, :with_active_consumer_role) } | ||
let(:family) { FactoryBot.create(:family, :with_primary_family_member, person: primary) } | ||
|
||
describe '#call' do | ||
context 'success' do | ||
let(:hbx_id) { family.primary_person.hbx_id } | ||
|
||
it 'publishes the event successfully' do | ||
result = subject.call(hbx_id: hbx_id) | ||
expect(result.success).to eq( | ||
"Successfully published event: events.families.created_or_updated for family with primary person hbx_id: #{hbx_id}" | ||
) | ||
end | ||
end | ||
|
||
context 'failure' do | ||
context 'when a person does not exist with the given hbx_id' do | ||
it 'returns failure' do | ||
result = subject.call(hbx_id: 'primary.hbx_id') | ||
expect(result.failure).to eq( | ||
"Provide a valid person_hbx_id to fetch person. Invalid input hbx_id: primary.hbx_id" | ||
) | ||
end | ||
end | ||
|
||
context 'when person does not have primary family' do | ||
it 'returns failure' do | ||
result = subject.call(hbx_id: primary.hbx_id) | ||
expect(result.failure).to eq( | ||
"Primary Family does not exist with given hbx_id: #{primary.hbx_id}" | ||
) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rails_helper' | ||
|
||
RSpec.describe Operations::Crm::ForceSync do | ||
include Dry::Monads[:result] | ||
|
||
before :all do | ||
DatabaseCleaner.clean | ||
end | ||
|
||
after :all do | ||
DatabaseCleaner.clean | ||
end | ||
|
||
describe '#call' do | ||
context 'success' do | ||
let(:primary1_hbx_id) { 'primary1' } | ||
let(:primary2_hbx_id) { 'primary2' } | ||
let(:primary3_hbx_id) { 'primary3' } | ||
let(:params) { { primary_hbx_ids: [primary1_hbx_id, primary2_hbx_id, primary3_hbx_id] } } | ||
|
||
let(:publish_instance) { instance_double(Operations::Crm::Family::Publish) } | ||
|
||
let(:message1) { "Successfully published event: events.families.created_or_updated for family with primary person hbx_id: #{primary1_hbx_id}" } | ||
let(:message2) { "Provide a valid person_hbx_id to fetch person. Invalid input hbx_id: primary2_hbx_id" } | ||
let(:message3) { "Primary Family does not exist with given hbx_id: #{primary3_hbx_id}" } | ||
|
||
before do | ||
allow(::Operations::Crm::Family::Publish).to receive(:new).and_return(publish_instance) | ||
allow(publish_instance).to receive(:call).with(hbx_id: primary1_hbx_id).and_return( | ||
Success(message1) | ||
) | ||
|
||
allow(publish_instance).to receive(:call).with(hbx_id: primary2_hbx_id).and_return( | ||
Failure(message2) | ||
) | ||
|
||
allow(publish_instance).to receive(:call).with(hbx_id: primary3_hbx_id).and_return( | ||
Failure(message3) | ||
) | ||
end | ||
|
||
it 'returns a success monad' do | ||
expect(subject.call(params).success?).to be_truthy | ||
end | ||
|
||
it 'creates a CSV ' do | ||
message = subject.call(params).success | ||
csv_file_name = message.split(': ').last | ||
expect( | ||
File.exist?(csv_file_name) | ||
).to be_truthy | ||
end | ||
|
||
it 'logs the results of the operation in CSV' do | ||
message = subject.call(params).success | ||
csv_file_name = message.split(': ').last | ||
csv = CSV.read(csv_file_name) | ||
expect(csv[1]).to eq([primary1_hbx_id, 'Success', message1]) | ||
expect(csv[2]).to eq([primary2_hbx_id, 'Failed', message2]) | ||
expect(csv[3]).to eq([primary3_hbx_id, 'Failed', message3]) | ||
end | ||
end | ||
|
||
context 'failure' do | ||
context 'when primary_hbx_ids is not an array' do | ||
let(:params) { { primary_hbx_ids: 'primary1' } } | ||
|
||
it 'returns a failure monad' do | ||
expect(subject.call(params).failure).to eq('Invalid input for primary_hbx_ids: primary1. Provide an array of HBX IDs.') | ||
end | ||
end | ||
|
||
context 'when primary_hbx_ids is an empty array' do | ||
let(:params) { { primary_hbx_ids: [] } } | ||
|
||
it 'returns a failure monad' do | ||
expect(subject.call(params).failure).to eq('Invalid input for primary_hbx_ids: []. Provide an array of HBX IDs.') | ||
end | ||
end | ||
|
||
context 'when primary_hbx_ids array has non-string elements' do | ||
let(:params) { { primary_hbx_ids: [100] } } | ||
|
||
it 'returns a failure monad' do | ||
expect(subject.call(params).failure).to eq('Invalid input for primary_hbx_ids: [100]. Provide an array of HBX IDs.') | ||
end | ||
end | ||
end | ||
end | ||
|
||
after :all do | ||
# Clean up the CSV files created during the test | ||
Dir.glob("#{Rails.root}/crm_force_sync_*.csv").each do |file| | ||
FileUtils.rm(file) | ||
end | ||
end | ||
end |