• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pulibrary / orangelight / 416cc356-3bf9-484d-9f55-13fb69a908aa

30 Oct 2025 03:25PM UTC coverage: 95.394% (+0.02%) from 95.379%
416cc356-3bf9-484d-9f55-13fb69a908aa

Pull #5281

circleci

christinach
Fix the bearer failure. Use the message from the json response
and build the alert element and display in JS
Use textContent see https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#differences_from_innertext

Co-authored-by: Jane Sandberg <sandbergja@users.noreply.github.com>
Pull Request #5281: Don't use Rails ujs for request form submission

56 of 57 new or added lines in 1 file covered. (98.25%)

1 existing line in 1 file now uncovered.

6213 of 6513 relevant lines covered (95.39%)

1461.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.4
/app/controllers/requests/form_controller.rb
1
# frozen_string_literal: true
2
require 'faraday'
3✔
3

4
include Requests::ApplicationHelper
3✔
5

6
module Requests
3✔
7
  class FormController < ApplicationController
3✔
8
    before_action :authenticate_user!, except: [:index], unless: -> { aeon? }
138✔
9

10
    def index
3✔
11
      redirect_to('/')
1✔
12
    end
13

14
    def generate
3✔
15
      # Patron can be slow to load, start loading it early
16
      @user = current_or_guest_user
87✔
17
      patron_request = Thread.new { Patron.authorize(user: @user) }
174✔
18

19
      system_id = sanitize(params[:system_id])
87✔
20
      mfhd = sanitize(params[:mfhd])
87✔
21
      params.require(:mfhd) unless system_id.starts_with?("SCSB") # there are not multiple locations for shared items so no MFHD is passed
87✔
22
      @back_to_record_url = BackToRecordUrl.new(params)
86✔
23

24
      @title = "Request ID: #{system_id}"
86✔
25

26
      # needed to see if we can suppress login for this item
27
      @request = FormDecorator.new(Requests::Form.new(system_id:, mfhd:, patron_request:), view_context, @back_to_record_url)
86✔
28
      @patron = patron_request.value
86✔
29
      patron_errors = @patron.errors
86✔
30
      flash.now[:error] = patron_errors.join(", ") if patron_errors.present?
86✔
31
    rescue ActionController::ParameterMissing
32
      render 'requests/form/no_location_specified'
1✔
33
    end
34

35
    def aeon?
3✔
36
      return true if params["aeon"] == 'true'
135✔
37

38
      false
134✔
39
    end
40

41
    # will post and a JSON document of selected "requestable" objects with selection parameters and
42
    # user information for further processing and distribution to various request endpoints.
43
    def submit
3✔
44
      @submission = Requests::Submission.new(sanitize_submission(params), Patron.new(user: current_or_guest_user))
45✔
45

46
      valid = @submission.valid?
45✔
47
      @services = @submission.process_submission if valid
45✔
48

49
      response_data = if valid && @submission.service_errors.blank?
45✔
50
                        respond_to_submit_success(@submission)
37✔
51
                      elsif valid # submission was valid, but service failed
8✔
52
                        respond_to_service_error(@services)
4✔
53
                      else
54
                        respond_to_validation_error(@submission)
4✔
55
                      end
56

57
      render json: response_data
45✔
58
    end
59

60
    private
3✔
61

62
      def mode
3✔
63
        return 'standard' if params[:mode].nil?
×
64
        sanitize(params[:mode])
×
65
      end
66

67
      # trusted params
68
      def request_params
3✔
69
        params.permit(:id, :system_id, :mfhd, :user_name, :email, :loc_code, :user, :requestable, :request, :barcode, :isbns).permit!
×
70
      end
71

72
      def sanitize_submission(params)
3✔
73
        params[:requestable].each do |requestable|
45✔
74
          params['user_supplied_enum'] = sanitize(requestable['user_supplied_enum']) if requestable.key? 'user_supplied_enum'
215✔
75
        end
76
        lparams = params.permit(bib: [:id, :title, :author, :isbn, :date])
45✔
77
        lparams[:requestable] = params[:requestable].map do |requestable|
45✔
78
          json_pick_up = requestable[:pick_up]
215✔
79
          requestable = requestable.merge(JSON.parse(json_pick_up)) if json_pick_up.present?
215✔
80
          requestable.permit!
215✔
81
        end
82
        lparams
45✔
83
      end
84

85
      # :reek:UncommunicativeVariableName { accept: ['e'] }
86
      # :reek:TooManyStatements
87
      def respond_to_submit_success(submission)
3✔
88
        success_message = submission.success_messages.join(' ')
37✔
89
        flash.now[:success] = success_message
37✔
90
        logger.info "Request Sent"
37✔
91

92
        begin
93
          flash_html = render_to_string(partial: 'flash_messages')
37✔
94
        rescue StandardError => e
95
          logger.error "Failed to render flash_messages partial: #{e.message}"
3✔
96
          flash_html = "<div class='alert alert-success' aria-live='polite'>#{success_message}<button class='close' data-bs-dismiss='alert'>&times;</button></div>"
3✔
97
        end
98

99
        {
37✔
100
          success: true,
101
          message: success_message,
102
          flash_messages_html: flash_html
103
        }
104
      end
105

106
      # :reek:UncommunicativeVariableName { accept: ['e'] }
107
      def respond_to_service_error(services)
3✔
108
        errors = services.map(&:errors).flatten
4✔
109
        error_types = errors.pluck(:type).uniq
4✔
110
        flash_now_error = if error_types.include?("digitize")
4✔
111
                            errors[error_types.index("digitize")][:error]
3✔
112
                          else
113
                            I18n.t('requests.submit.service_error')
1✔
114
                          end
115
        flash.now[:error] = flash_now_error
4✔
116
        logger.error "Request Service Error"
4✔
117
        service_errors = services.map(&:error_hash).inject(:merge)
4✔
118
        send_error_email(service_errors, @submission)
4✔
119

120
        begin
121
          flash_html = render_to_string(partial: 'flash_messages')
4✔
122
        rescue StandardError => e
123
          logger.error "Failed to render flash_messages partial: #{e.message}"
2✔
124
          flash_html = "<div class='alert alert-danger' aria-live='polite'>#{flash_now_error}<button class='close' data-bs-dismiss='alert'>&times;</button></div>"
2✔
125
        end
126

127
        {
4✔
128
          success: false,
129
          message: flash_now_error,
130
          errors: service_errors,
131
          flash_messages_html: flash_html
132
        }
133
      end
134

135
      # :reek:TooManyStatements
136
      def respond_to_validation_error(submission)
3✔
137
        error_message = I18n.t('requests.submit.error')
4✔
138
        error_messages = submission.errors.messages
4✔
139
        specific_errors = extract_specific_errors(error_messages)
4✔
140

141
        flash.now[:error] = error_message
4✔
142
        logger.error "Request Submission #{error_messages.as_json}"
4✔
143

144
        flash_html = render_flash_messages_with_fallback(error_message, specific_errors)
4✔
145

146
        {
4✔
147
          success: false,
148
          message: error_message,
149
          errors: format_validation_errors(error_messages),
150
          flash_messages_html: flash_html
151
        }
152
      end
153

154
      def sanitize(str)
3✔
155
        str.gsub(/[^A-Za-z0-9@\-_\.]/, '') if str.is_a? String
182✔
156
        str
182✔
157
      end
158

159
      # :reek:NestedIterators
160
      # :reek:TooManyStatements
161
      # :reek:UtilityFunction
162
      def format_validation_errors(error_messages)
3✔
163
        formatted_errors = {}
7✔
164

165
        error_messages.each do |key, values|
7✔
166
          formatted_errors[key] = if key == :items
9✔
167
                                    # Handle special items field format
168
                                    values.map do |value|
6✔
169
                                      if value.is_a?(Hash)
6✔
170
                                        first_value = value.values.first
5✔
171
                                        {
172
                                          key: value.keys.first,
5✔
173
                                          type: first_value['type'],
174
                                          text: first_value['text']
175
                                        }
176
                                      else
177
                                        { text: value }
1✔
178
                                      end
179
                                    end
180
                                  else
181
                                    # Handle regular validation errors
182
                                    values
3✔
183
                                  end
184
        end
185
        formatted_errors
7✔
186
      end
187

188
      # :reek:NestedIterators
189
      # :reek:TooManyStatements
190
      # :reek:UtilityFunction
191
      def extract_specific_errors(error_messages)
3✔
192
        specific_errors = []
4✔
193
        error_messages.each do |key, values|
4✔
194
          if key == :items
5✔
195
            values.each do |value|
4✔
196
              if value.is_a?(Hash)
4✔
197
                first_value = value.values.first
4✔
198
                text_value = first_value['text']
4✔
199
                specific_errors << text_value if text_value
4✔
200
              else
NEW
UNCOV
201
                specific_errors << value
×
202
              end
203
            end
204
          else
205
            specific_errors.concat(values)
1✔
206
          end
207
        end
208
        specific_errors
4✔
209
      end
210

211
      # :reek:UncommunicativeVariableName { accept: ['e'] }
212
      # :reek:TooManyStatements
213
      def render_flash_messages_with_fallback(error_message, specific_errors)
3✔
214
        render_to_string(partial: 'flash_messages')
4✔
215
      rescue StandardError => e
216
        logger.error "Failed to render flash_messages partial: #{e.message}"
4✔
217
        # Include specific errors in fallback HTML
218
        error_list = specific_errors.map { |err| "<li>#{err}</li>" }.join
9✔
219
        "<div class='alert alert-danger' aria-live='polite'>#{error_message}<button class='close' data-bs-dismiss='alert'>&times;</button><ul>#{error_list}</ul></div>"
4✔
220
      end
221

222
      # This has to be a utility function to prevent ActiveJob from trying to serialize too many objects
223
      # :reek:UtilityFunction
224
      def send_error_email(errors, submission)
3✔
225
        Requests::RequestMailer.send("service_error_email", errors, submission.to_h).deliver_later
4✔
226
      end
227
  end
228
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc