diff --git a/app/controllers/concerns/idv/step_indicator_concern.rb b/app/controllers/concerns/idv/step_indicator_concern.rb index 6d95ba445f8..8329f6af596 100644 --- a/app/controllers/concerns/idv/step_indicator_concern.rb +++ b/app/controllers/concerns/idv/step_indicator_concern.rb @@ -20,13 +20,21 @@ module StepIndicatorConcern { name: :secure_account }, ].freeze + STEP_INDICATOR_STEPS_IPP = [ + { name: :find_a_post_office }, + { name: :verify_info }, + { name: :verify_phone }, + { name: :re_enter_password }, + { name: :go_to_the_post_office }, + ].freeze + included do helper_method :step_indicator_steps end def step_indicator_steps if in_person_proofing? - Idv::Flows::InPersonFlow::STEP_INDICATOR_STEPS + Idv::StepIndicatorConcern::STEP_INDICATOR_STEPS_IPP elsif gpo_address_verification? Idv::StepIndicatorConcern::STEP_INDICATOR_STEPS_GPO else diff --git a/app/controllers/idv/by_mail/request_letter_controller.rb b/app/controllers/idv/by_mail/request_letter_controller.rb index 77197dbb477..a7ae8df4857 100644 --- a/app/controllers/idv/by_mail/request_letter_controller.rb +++ b/app/controllers/idv/by_mail/request_letter_controller.rb @@ -59,11 +59,7 @@ def confirm_letter_sends_allowed end def step_indicator_steps - if in_person_proofing? - Idv::Flows::InPersonFlow::STEP_INDICATOR_STEPS_GPO - else - StepIndicatorConcern::STEP_INDICATOR_STEPS_GPO - end + StepIndicatorConcern::STEP_INDICATOR_STEPS_GPO end end end diff --git a/app/controllers/idv/by_mail/resend_letter_controller.rb b/app/controllers/idv/by_mail/resend_letter_controller.rb index 9e583cc46af..4faed1d29fd 100644 --- a/app/controllers/idv/by_mail/resend_letter_controller.rb +++ b/app/controllers/idv/by_mail/resend_letter_controller.rb @@ -83,11 +83,7 @@ def pii_locked? end def step_indicator_steps - if in_person_proofing? - Idv::Flows::InPersonFlow::STEP_INDICATOR_STEPS_GPO - else - StepIndicatorConcern::STEP_INDICATOR_STEPS_GPO - end + StepIndicatorConcern::STEP_INDICATOR_STEPS_GPO end end end diff --git a/app/controllers/idv/in_person_controller.rb b/app/controllers/idv/in_person_controller.rb index 3ee3e59b932..b1091df590b 100644 --- a/app/controllers/idv/in_person_controller.rb +++ b/app/controllers/idv/in_person_controller.rb @@ -9,21 +9,16 @@ class InPersonController < ApplicationController before_action :confirm_two_factor_authenticated before_action :redirect_unless_enrollment - - include IdvSessionConcern - include Flow::FlowStateMachine - include ThreatMetrixConcern - - before_action :redirect_if_flow_completed - + before_action :initialize_in_person_session before_action :set_usps_form_presenter - FLOW_STATE_MACHINE_SETTINGS = { - step_url: :idv_in_person_step_url, - final_url: :idv_in_person_state_id_url, - flow: Idv::Flows::InPersonFlow, - analytics_id: 'In Person Proofing', - }.freeze + def index + redirect_to idv_in_person_state_id_url + end + + def update + redirect_to idv_in_person_state_id_url + end private @@ -31,8 +26,8 @@ def redirect_unless_enrollment redirect_to idv_url unless current_user.establishing_in_person_enrollment end - def redirect_if_flow_completed - flow_finish if idv_session.applicant + def initialize_in_person_session + user_session['idv/in_person'] ||= { pii_from_user: { uuid: current_user.uuid } } end def set_usps_form_presenter diff --git a/app/services/flow/base_flow.rb b/app/services/flow/base_flow.rb deleted file mode 100644 index 18e201ed721..00000000000 --- a/app/services/flow/base_flow.rb +++ /dev/null @@ -1,85 +0,0 @@ -# frozen_string_literal: true - -module Flow - class BaseFlow - include Failure - - attr_accessor :flow_session - attr_reader :current_user, :current_sp, :params, :request, :json, - :http_status, :controller - - def initialize(controller, session) - @controller = controller - @redirect = nil - @json = nil - @flow_session = session - end - - def next_step - return @redirect if @redirect - step, _klass = steps.find do |_step, klass| - !@flow_session[klass.to_s] - end - step - end - - def redirect_to(url) - @redirect = url - end - - def render_json(json, status: nil) - @json = json - @http_status = status || :ok - end - - def step_handler(step) - steps[step] || actions[step] - end - - def step_handler_instance(step) - @step_handler_instances ||= {} - @step_handler_instances[step] ||= step_handler(step)&.new(self) - end - - def handle(step) - @flow_session[:error_message] = nil - handler = step_handler_instance(step) - return failure("Unhandled step #{step}") unless handler - wrap_send(handler) - end - - def extra_view_variables(step) - handler = step_handler_instance(step) - return failure("Unhandled step #{step}") unless handler - handler.extra_view_variables - end - - def extra_analytics_properties - {} - end - - def flow_path - 'standard' - end - - private - - def wrap_send(handler) - value = handler.base_call - form_response(handler, value) - end - - def form_response(obj, value) - response = BaseStep.acceptable_response_object?(value) ? value : successful_response - obj.mark_step_complete if response.success? - response - end - - def successful_response - FormResponse.new(success: true) - end - - delegate :flash, :session, :current_user, :current_sp, :params, :request, - :poll_with_meta_refresh, :analytics, to: :@controller - end -end diff --git a/app/services/flow/base_step.rb b/app/services/flow/base_step.rb deleted file mode 100644 index b273da8fd12..00000000000 --- a/app/services/flow/base_step.rb +++ /dev/null @@ -1,86 +0,0 @@ -# frozen_string_literal: true - -module Flow - class BaseStep - include Rails.application.routes.url_helpers - include Failure - - def initialize(flow, name) - @flow = flow - @name = name - end - - def base_call - form_response = form_submit - unless form_response.success? - flow_session[:error_message] = form_response.first_error_message - return form_response - end - create_response(form_response, call) - end - - def mark_step_complete(step = nil) - klass = step.nil? ? self.class : steps[step] - flow_session[klass.to_s] = true - end - - def mark_step_incomplete(step = nil) - klass = step.nil? ? self.class : steps[step] - flow_session.delete(klass.to_s) - nil - end - - def self.acceptable_response_object?(obj) - obj.is_a?(FormResponse) || obj.is_a?(DocAuth::Response) - end - - # Return a hash of local variables required for step view template - def extra_view_variables - {} - end - - def url_options - @flow.controller.url_options - end - - delegate :analytics_visited_event, :analytics_submitted_event, to: :class - - private - - def create_response(form_submit_response, call_response) - return form_submit_response unless BaseStep.acceptable_response_object?(call_response) - form_submit_response.merge(call_response) - end - - def form_submit - FormResponse.new(success: true) - end - - def flow_params - params[@name] - end - - def permit(*args) - params.require(:doc_auth).permit(*args) - end - - def redirect_to(url) - @flow.redirect_to(url) - end - - def render_json(json, status: nil) - @flow.render_json(json, status: status) - end - - def reset - @flow.flow_session = {} - end - - def amzn_trace_id - request.headers['X-Amzn-Trace-Id'] - end - - delegate :flash, :session, :flow_session, :current_user, :current_sp, :params, :steps, :request, - :poll_with_meta_refresh, to: :@flow - end -end diff --git a/app/services/flow/failure.rb b/app/services/flow/failure.rb deleted file mode 100644 index 23ac1d45e92..00000000000 --- a/app/services/flow/failure.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -module Flow - module Failure - private - - def failure(message, extra = nil) - flow_session[:error_message] = message - form_response_params = { success: false, errors: { message: message } } - form_response_params[:extra] = extra unless extra.nil? - FormResponse.new(**form_response_params) - end - end -end diff --git a/app/services/flow/flow_state_machine.rb b/app/services/flow/flow_state_machine.rb deleted file mode 100644 index 0eab05f6c43..00000000000 --- a/app/services/flow/flow_state_machine.rb +++ /dev/null @@ -1,219 +0,0 @@ -# frozen_string_literal: true - -module Flow - module FlowStateMachine - extend ActiveSupport::Concern - include OptInHelper - - included do - before_action :initialize_flow_state_machine - before_action :ensure_correct_step, only: :show - end - - attr_accessor :flow - - def index - redirect_to idv_in_person_state_id_url - end - - def show - track_step_visited - render_step(current_step, flow.flow_session) - end - - def update - step = current_step - - return render_not_found unless flow.step_handler_instance(step).present? - - result = flow.handle(step) - - analytics.public_send( - flow.step_handler_instance(step).analytics_submitted_event, - **result.to_h.merge(analytics_properties), - ) - - register_update_step(step, result) - if flow.json - render json: flow.json, status: flow.http_status - return - end - flow_finish and return unless next_step - render_update(step, result) - end - - def poll_with_meta_refresh(seconds) - @meta_refresh = seconds - end - - private - - def current_step - params[:step]&.underscore - end - - def track_step_visited - analytics.public_send( - flow.step_handler(current_step).analytics_visited_event, - **analytics_properties, - ) - - Funnel::DocAuth::RegisterStep.new(user_id, issuer).call(current_step, :view, true) - end - - def user_id - current_user ? current_user.id : user_id_from_token - end - - def user_id_from_token - current_session[:doc_capture_user_id] - end - - def register_update_step(step, result) - Funnel::DocAuth::RegisterStep.new(user_id, issuer).call(step, :update, result.success?) - end - - def issuer - sp_session[:issuer] - end - - def initialize_flow_state_machine - klass = self.class - flow = klass::FLOW_STATE_MACHINE_SETTINGS[:flow] - @name = klass.name.underscore.gsub('_controller', '') - @namespace = flow.name.split('::').first.underscore - @step_url = klass::FLOW_STATE_MACHINE_SETTINGS[:step_url] - @final_url = klass::FLOW_STATE_MACHINE_SETTINGS[:final_url] - @analytics_id = klass::FLOW_STATE_MACHINE_SETTINGS[:analytics_id] - @view = klass::FLOW_STATE_MACHINE_SETTINGS[:view] - - current_session[@name] ||= {} - @flow = flow.new(self, current_session, @name) - end - - def render_update(step, result) - redirect_to next_step and return if next_step_is_url - move_to_next_step and return if result.success? - ensure_correct_step and return - set_error_and_render(step, result) - end - - def set_error_and_render(step, result) - flow_session = flow.flow_session - flow_session[:error_message] = result.first_error_message - render_step(step, flow_session) - end - - def move_to_next_step - current_session[@name] = flow.flow_session - redirect_to_step(next_step) - end - - def render_step(step, flow_session) - @params = params - @request = request - return if call_optional_show_step(step) - step_params = flow.extra_view_variables(step) - local_params = step_params.merge( - flow_namespace: @namespace, - flow_session: flow_session, - step_indicator: step_indicator_params, - step_template: "#{@view || @name}/#{step}", - ) - render template: 'layouts/flow_step', locals: local_params - end - - def call_optional_show_step(optional_step) - return unless @flow.class.const_defined?(:OPTIONAL_SHOW_STEPS) - optional_show_step = @flow.class::OPTIONAL_SHOW_STEPS.with_indifferent_access[optional_step] - return unless optional_show_step - result = optional_show_step.new(@flow).base_call - - optional_show_step_name = optional_show_step.to_s.demodulize.underscore - optional_properties = result.to_h.merge( - step: optional_show_step_name, - analytics_id: @analytics_id, - ) - - analytics.public_send( - optional_show_step.analytics_optional_step_event, - **optional_properties, - ) - - if next_step.to_s != optional_step - if next_step_is_url - redirect_to next_step - else - redirect_to_step(next_step) - end - return true - end - false - end - - def step_indicator_params - return if !flow.class.const_defined?(:STEP_INDICATOR_STEPS) - handler = flow.step_handler(current_step) - return if !handler || !handler.const_defined?(:STEP_INDICATOR_STEP) - { - steps: flow.class::STEP_INDICATOR_STEPS, - current_step: handler::STEP_INDICATOR_STEP, - } - end - - def ensure_correct_step - redirect_to_step(next_step) if next_step.to_s != current_step - end - - def flow_finish - redirect_to send(@final_url) - end - - def redirect_to_step(_step) - flow_finish and return unless next_step - redirect_url - end - - def redirect_url - redirect_to idv_in_person_state_id_url - end - - def analytics_properties - { - flow_path: flow.flow_path, - step: current_step, - analytics_id: @analytics_id, - }.merge(flow.extra_analytics_properties) - .merge(**opt_in_analytics_properties) - end - - def current_step_name - "#{current_step}_#{action_name}" - end - - def next_step - flow.next_step - end - - def next_step_is_url - next_step.to_s.index(':') - end - - def current_session - user_session || session - end - end -end - -# sample usage: -# -# class FooController -# include Flow::FlowStateMachine -# -# FLOW_STATE_MACHINE_SETTINGS = { -# step_url: :foo_step_url, -# final_url: :after_foo_url, -# flow: FooFlow, -# analytics_id: Analytics::FOO, -# }.freeze -# end diff --git a/app/views/idv/in_person/address/show.html.erb b/app/views/idv/in_person/address/show.html.erb index e5a7ab2be63..4a06cea6aa0 100644 --- a/app/views/idv/in_person/address/show.html.erb +++ b/app/views/idv/in_person/address/show.html.erb @@ -1,6 +1,6 @@ <% content_for(:pre_flash_content) do %> <%= render StepIndicatorComponent.new( - steps: Idv::Flows::InPersonFlow::STEP_INDICATOR_STEPS, + steps: Idv::StepIndicatorConcern::STEP_INDICATOR_STEPS_IPP, current_step: :verify_info, locale_scope: 'idv', class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4', diff --git a/app/views/idv/in_person/state_id/show.html.erb b/app/views/idv/in_person/state_id/show.html.erb index ebcf6df6ed3..a3400b9d881 100644 --- a/app/views/idv/in_person/state_id/show.html.erb +++ b/app/views/idv/in_person/state_id/show.html.erb @@ -1,6 +1,6 @@ <% content_for(:pre_flash_content) do %> <%= render StepIndicatorComponent.new( - steps: Idv::Flows::InPersonFlow::STEP_INDICATOR_STEPS, + steps: Idv::StepIndicatorConcern::STEP_INDICATOR_STEPS_IPP, current_step: :verify_info, locale_scope: 'idv', class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4', diff --git a/app/views/idv/shared/_back.html.erb b/app/views/idv/shared/_back.html.erb index 88f45338b35..c022091574a 100644 --- a/app/views/idv/shared/_back.html.erb +++ b/app/views/idv/shared/_back.html.erb @@ -11,9 +11,8 @@ locals: * fallback_path: (Optional) Path to redirect absent action, step, and HTTP referer. %> <% text = '‹ ' + t('forms.buttons.back') - step_url = local_assigns[:step_url] || @step_url step = local_assigns[:action] || local_assigns[:step] - path = (step_url && step) ? send(step_url, step: step) : go_back_path + path = go_back_path path ||= local_assigns[:fallback_path] classes = [] classes << local_assigns[:class] if local_assigns[:class] %> diff --git a/config/routes.rb b/config/routes.rb index 78c4010b282..48349ed90a5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -419,6 +419,7 @@ put '/in_person_proofing/state_id' => redirect('verify/in_person/state_id', status: 307) get '/in_person' => 'in_person#index' + put '/in_person' => 'in_person#update' get '/in_person/ready_to_verify' => 'in_person/ready_to_verify#show', as: :in_person_ready_to_verify post '/in_person/usps_locations' => 'in_person/usps_locations#index' @@ -431,8 +432,6 @@ put '/in_person/ssn' => 'in_person/ssn#update' get '/in_person/verify_info' => 'in_person/verify_info#show' put '/in_person/verify_info' => 'in_person/verify_info#update' - get '/in_person/:step' => 'in_person#show', as: :in_person_step - put '/in_person/:step' => 'in_person#update' get '/by_mail/enter_code' => 'by_mail/enter_code#index', as: :verify_by_mail_enter_code post '/by_mail/enter_code' => 'by_mail/enter_code#create' diff --git a/spec/controllers/idv/in_person_controller_spec.rb b/spec/controllers/idv/in_person_controller_spec.rb index 462a79c83eb..6c010509219 100644 --- a/spec/controllers/idv/in_person_controller_spec.rb +++ b/spec/controllers/idv/in_person_controller_spec.rb @@ -17,9 +17,9 @@ expect(subject).to have_actions( :before, :confirm_two_factor_authenticated, - :initialize_flow_state_machine, - :ensure_correct_step, :set_usps_form_presenter, + :redirect_unless_enrollment, + :initialize_in_person_session, ) end end @@ -99,7 +99,7 @@ end it 'finishes the flow' do - put :update, params: { step: 'state_id' } + put :update expect(response).to redirect_to idv_in_person_state_id_path end diff --git a/spec/features/idv/in_person_threatmetrix_spec.rb b/spec/features/idv/in_person_threatmetrix_spec.rb index af9cb2644c2..2e3ec70ce93 100644 --- a/spec/features/idv/in_person_threatmetrix_spec.rb +++ b/spec/features/idv/in_person_threatmetrix_spec.rb @@ -211,9 +211,9 @@ def deactivate_profile_update_enrollment(status:) visit_idp_from_sp_with_ial2(sp) expect(page).to have_current_path(idv_please_call_path) - page.visit('/verify/welcome') + page.visit(idv_welcome_path) expect(page).to have_current_path(idv_please_call_path) - page.visit('/verify/in_person/document_capture') + page.visit(idv_in_person_state_id_path) expect(page).to have_current_path(idv_please_call_path) end @@ -226,7 +226,7 @@ def deactivate_profile_update_enrollment(status:) expect do review_pass.run(args: [user.uuid], config:) end.to(change { ActionMailer::Base.deliveries.count }.by(1)) - page.visit('/verify/welcome') + page.visit(idv_welcome_path) expect(page).to have_current_path(idv_activated_path) end end @@ -310,9 +310,9 @@ def deactivate_profile_update_enrollment(status:) visit_idp_from_sp_with_ial2(sp) expect(page).to have_current_path(idv_not_verified_path) - page.visit('/verify/welcome') + page.visit(idv_welcome_path) expect(page).to have_current_path(idv_not_verified_path) - page.visit('/verify/in_person/document_capture') + page.visit(idv_in_person_state_id_path) expect(page).to have_current_path(idv_not_verified_path) end end diff --git a/spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb b/spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb index 152365c4d20..4379ef66113 100644 --- a/spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb +++ b/spec/views/idv/in_person/ready_to_verify/show.html.erb_spec.rb @@ -29,7 +29,7 @@ is_enhanced_ipp: is_enhanced_ipp, ) end - let(:step_indicator_steps) { Idv::Flows::InPersonFlow::STEP_INDICATOR_STEPS } + let(:step_indicator_steps) { Idv::StepIndicatorConcern::STEP_INDICATOR_STEPS_IPP } let(:sp_event_name) { 'IdV: user clicked sp link on ready to verify page' } let(:help_event_name) { 'IdV: user clicked what to bring link on ready to verify page' } diff --git a/spec/views/idv/shared/_back.html.erb_spec.rb b/spec/views/idv/shared/_back.html.erb_spec.rb index 2741c0da966..bd7e24cab3e 100644 --- a/spec/views/idv/shared/_back.html.erb_spec.rb +++ b/spec/views/idv/shared/_back.html.erb_spec.rb @@ -1,7 +1,6 @@ require 'rails_helper' RSpec.describe 'idv/doc_auth/_back.html.erb' do - let(:step_url) { nil } let(:action) { nil } let(:step) { nil } let(:classes) { nil } @@ -9,7 +8,6 @@ subject do render 'idv/shared/back', { - step_url: step_url, action: action, step: step, class: classes, @@ -25,78 +23,6 @@ end end - context 'with step URL in locals' do - let(:step_url) { :idv_in_person_step_url } - - context 'with action' do - let(:action) { 'redo_ssn' } - - it 'renders' do - expect(subject).to have_selector( - "form[action='#{send(:idv_in_person_step_url, step: 'redo_ssn')}']", - ) - expect(subject).to have_selector('input[name="_method"][value="put"]', visible: false) - expect(subject).to have_selector("[type='submit']") - expect(subject).to have_selector('button', text: '‹ ' + t('forms.buttons.back')) - end - - it_behaves_like 'back link with class' - end - - context 'with step' do - let(:step) { 'verify' } - - it 'renders' do - expect(subject).to have_selector( - "a[href='#{send( - :idv_in_person_step_url, - step: 'verify', - )}']", - ) - expect(subject).to have_content('‹ ' + t('forms.buttons.back')) - end - - it_behaves_like 'back link with class' - end - end - - context 'with step URL in instance variable' do - before do - assign(:step_url, :idv_in_person_step_url) - end - - context 'with action' do - let(:action) { 'redo_ssn' } - - it 'renders' do - expect(subject).to have_selector( - "form[action='#{send(:idv_in_person_step_url, step: 'redo_ssn')}']", - ) - expect(subject).to have_selector('input[name="_method"][value="put"]', visible: false) - expect(subject).to have_selector("[type='submit']") - expect(subject).to have_selector('button', text: '‹ ' + t('forms.buttons.back')) - end - - it_behaves_like 'back link with class' - end - - context 'with step' do - let(:step) { 'verify' } - - it 'renders' do - expect(subject).to have_selector( - "a[href='#{send( - :idv_in_person_step_url, - step: 'verify', - )}']", - ) - expect(subject).to have_content('‹ ' + t('forms.buttons.back')) - end - - it_behaves_like 'back link with class' - end - end - context 'with back path' do before do allow(view).to receive(:go_back_path).and_return('/example') diff --git a/spec/views/idv/shared/ssn.html.erb_spec.rb b/spec/views/idv/shared/ssn.html.erb_spec.rb index c951b33ba9f..e0bff6b6f6d 100644 --- a/spec/views/idv/shared/ssn.html.erb_spec.rb +++ b/spec/views/idv/shared/ssn.html.erb_spec.rb @@ -30,7 +30,7 @@ :ssn_presenter, Idv::SsnPresenter.new( sp_name: sp_name, ssn_form: Idv::SsnFormatForm.new(nil), - step_indicator_steps: Idv::Flows::InPersonFlow::STEP_INDICATOR_STEPS + step_indicator_steps: Idv::StepIndicatorConcern::STEP_INDICATOR_STEPS ), ) render template: 'idv/shared/ssn', locals: {