diff --git a/app/services/usps_confirmation_maker.rb b/app/services/usps_confirmation_maker.rb index f8e23deeee6..cca30bb2f9d 100644 --- a/app/services/usps_confirmation_maker.rb +++ b/app/services/usps_confirmation_maker.rb @@ -1,8 +1,12 @@ class UspsConfirmationMaker - def initialize(pii:, issuer:, profile:) + def initialize(pii:, issuer:, profile: nil, profile_id: nil, otp: nil) + raise ArgumentError 'must have either profile or profile_id' if !profile && !profile_id + @pii = pii @issuer = issuer @profile = profile + @profile_id = profile_id + @otp = otp end def otp @@ -12,17 +16,17 @@ def otp def perform UspsConfirmation.create!(entry: attributes) UspsConfirmationCode.create!( - profile: profile, + profile_id: profile&.id || profile_id, otp_fingerprint: Pii::Fingerprinter.fingerprint(otp), ) + update_proofing_cost end private - attr_reader :pii, :issuer, :profile + attr_reader :pii, :issuer, :profile, :profile_id - # This method is single statement spread across many lines for readability def attributes { address1: pii[:address1], @@ -43,7 +47,7 @@ def generate_otp end def update_proofing_cost - Db::ProofingCost::AddUserProofingCost.call(profile.user.id, :gpo_letter) + Db::ProofingCost::AddUserProofingCost.call(profile&.user&.id, :gpo_letter) Db::SpCost::AddSpCost.call(issuer, 2, :gpo_letter) end end diff --git a/app/services/usps_daily_test_sender.rb b/app/services/usps_daily_test_sender.rb new file mode 100644 index 00000000000..65264f39ced --- /dev/null +++ b/app/services/usps_daily_test_sender.rb @@ -0,0 +1,38 @@ +# Mails a letter to the designated receiver +class UspsDailyTestSender + def run + if valid_designated_receiver_pii? + UspsConfirmationMaker.new( + pii: designated_receiver_pii, + issuer: nil, + profile_id: -1, # profile_id can't be null on UspsConfirmationCode + otp: otp_from_date, + ).perform + else + raise 'missing valid designated receiver pii' + end + rescue => err + NewRelic::Agent.notice_error(err) + end + + # @return [String] 10-digit OTP from the date + # @example + # "JAN20_2020" + def otp_from_date(date = Time.zone.today) + date.strftime('%b%d_%Y').upcase + end + + # @return [Hash] + def designated_receiver_pii + @designated_receiver_pii ||= JSON.parse( + AppConfig.env.gpo_designated_receiver_pii, + symbolize_names: true, + ) + end + + def valid_designated_receiver_pii? + %i[first_name last_name address1 city state zipcode].all? do |key| + designated_receiver_pii[key].present? + end + end +end diff --git a/config/application.yml.default b/config/application.yml.default index 057e2843205..2600e3097ea 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -58,6 +58,7 @@ enable_load_testing_mode: 'false' event_disavowal_expiration_hours: '240' google_analytics_key: google_analytics_timeout: '5' +gpo_designated_receiver_pii: '{}' ial2_recovery_request_valid_for_minutes: '15' identity_pki_disabled: 'true' identity_pki_local_dev: 'false' diff --git a/config/initializers/job_configurations.rb b/config/initializers/job_configurations.rb index 7457e41b723..7dfcc668efa 100644 --- a/config/initializers/job_configurations.rb +++ b/config/initializers/job_configurations.rb @@ -6,9 +6,11 @@ name: 'Send GPO letter', interval: 24 * 60 * 60, timeout: 300, - callback: lambda { + callback: lambda do + UspsDailyTestSender.new.run + UspsConfirmationUploader.new.run unless CalendarService.weekend_or_holiday?(Time.zone.today) - }, + end, ) # Send account deletion confirmation notifications diff --git a/spec/services/usps_daily_test_sender_spec.rb b/spec/services/usps_daily_test_sender_spec.rb new file mode 100644 index 00000000000..03925ba4733 --- /dev/null +++ b/spec/services/usps_daily_test_sender_spec.rb @@ -0,0 +1,63 @@ +require 'rails_helper' + +RSpec.describe UspsDailyTestSender do + subject(:sender) { UspsDailyTestSender.new } + + let(:designated_receiver_pii) do + { + first_name: 'Emmy', + last_name: 'Examperson', + address1: '123 Main St', + city: 'Washington', + state: 'DC', + zipcode: '20001', + } + end + + before do + allow(AppConfig.env).to receive(:gpo_designated_receiver_pii). + and_return(designated_receiver_pii.to_json) + end + + describe '#run' do + it 'creates a USPS confirmation and code for the current date' do + expect { sender.run }. + to(change { UspsConfirmation.count }.by(1).and(change { UspsConfirmationCode.count }.by(1))) + + usps_confirmation_code = UspsConfirmationCode.find_by( + otp_fingerprint: Pii::Fingerprinter.fingerprint(sender.otp_from_date), + ) + expect(usps_confirmation_code).to_not be_nil + expect(usps_confirmation_code.profile_id).to eq(-1) + end + + context 'when the designed receiver PII is missing' do + let(:designated_receiver_pii) { '' } + + it 'does not create usps records' do + expect { sender.run }. + to(change { UspsConfirmation.count }.by(0). + and(change { UspsConfirmationCode.count }.by(0))) + end + + it 'does not blow up (so the calling job can continue normally) and notifies NewRelic' do + expect(NewRelic::Agent).to receive(:notice_error) + + expect { sender.run }.to_not raise_error + end + end + end + + describe '#designated_receiver_pii' do + it 'parses PII from the application config' do + expect(sender.designated_receiver_pii).to eq(designated_receiver_pii) + end + end + + describe '#otp_from_date' do + it 'formats the date as a 10-digit OTP' do + expect(sender.otp_from_date(Date.new(2020, 1, 1))).to eq('JAN01_2020') + expect(sender.otp_from_date(Date.new(2021, 7, 15))).to eq('JUL15_2021') + end + end +end