diff --git a/Gemfile b/Gemfile index 21382e178..3867f8ac3 100644 --- a/Gemfile +++ b/Gemfile @@ -6,7 +6,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby "3.4.9" # Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main' -gem "rails", "~> 7.2.2" +gem "rails", "~> 8.1.3" # Use postgresql as the database for Active Record gem "pg", "~> 1.1" # Use Puma as the app server @@ -44,7 +44,7 @@ gem "view_component", "~> 4.9" # Use the AWS S3 SDK as storage mechanism gem "aws-sdk-s3" # Track changes to models for auditing or versioning. -gem "paper_trail", "~> 15.2" +gem "paper_trail", "~> 17.0" # Store active record objects in version whodunnits gem "paper_trail-globalid" @@ -75,9 +75,6 @@ gem "unread" gem "nokogiri", "~> 1.19.1" gem "thor", "~> 1.4.0" -# Pinning until activesupport is updated to v8.1.2 -gem "connection_pool", "~> 2.5" - group :development, :test do # Check gems for known vulnerabilities gem "bundler-audit" diff --git a/Gemfile.lock b/Gemfile.lock index 4c3b075e2..afabb75a7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,83 +1,84 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.2.3.2) - actionpack (= 7.2.3.2) - activesupport (= 7.2.3.2) + action_text-trix (2.1.19) + railties + actioncable (8.1.3.1) + actionpack (= 8.1.3.1) + activesupport (= 8.1.3.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.2.3.2) - actionpack (= 7.2.3.2) - activejob (= 7.2.3.2) - activerecord (= 7.2.3.2) - activestorage (= 7.2.3.2) - activesupport (= 7.2.3.2) + actionmailbox (8.1.3.1) + actionpack (= 8.1.3.1) + activejob (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) mail (>= 2.8.0) - actionmailer (7.2.3.2) - actionpack (= 7.2.3.2) - actionview (= 7.2.3.2) - activejob (= 7.2.3.2) - activesupport (= 7.2.3.2) + actionmailer (8.1.3.1) + actionpack (= 8.1.3.1) + actionview (= 8.1.3.1) + activejob (= 8.1.3.1) + activesupport (= 8.1.3.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.2.3.2) - actionview (= 7.2.3.2) - activesupport (= 7.2.3.2) - cgi + actionpack (8.1.3.1) + actionview (= 8.1.3.1) + activesupport (= 8.1.3.1) nokogiri (>= 1.8.5) - racc - rack (>= 2.2.4, < 3.3) + rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (7.2.3.2) - actionpack (= 7.2.3.2) - activerecord (= 7.2.3.2) - activestorage (= 7.2.3.2) - activesupport (= 7.2.3.2) + actiontext (8.1.3.1) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.2.3.2) - activesupport (= 7.2.3.2) + actionview (8.1.3.1) + activesupport (= 8.1.3.1) builder (~> 3.1) - cgi erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.2.3.2) - activesupport (= 7.2.3.2) + activejob (8.1.3.1) + activesupport (= 8.1.3.1) globalid (>= 0.3.6) - activemodel (7.2.3.2) - activesupport (= 7.2.3.2) + activemodel (8.1.3.1) + activesupport (= 8.1.3.1) activemodel-serializers-xml (1.0.3) activemodel (>= 5.0.0.a) activesupport (>= 5.0.0.a) builder (~> 3.1) - activerecord (7.2.3.2) - activemodel (= 7.2.3.2) - activesupport (= 7.2.3.2) + activerecord (8.1.3.1) + activemodel (= 8.1.3.1) + activesupport (= 8.1.3.1) timeout (>= 0.4.0) - activestorage (7.2.3.2) - actionpack (= 7.2.3.2) - activejob (= 7.2.3.2) - activerecord (= 7.2.3.2) - activesupport (= 7.2.3.2) + activestorage (8.1.3.1) + actionpack (= 8.1.3.1) + activejob (= 8.1.3.1) + activerecord (= 8.1.3.1) + activesupport (= 8.1.3.1) marcel (~> 1.0) - activesupport (7.2.3.2) + activesupport (8.1.3.1) base64 - benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + json logger (>= 1.4.2) - minitest (>= 5.1, < 6) + minitest (>= 5.1) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) ast (2.4.3) @@ -115,7 +116,6 @@ GEM thread_safe (~> 0.3, >= 0.3.1) base64 (0.3.0) bcrypt (3.1.22) - benchmark (0.5.0) better_html (2.2.0) actionview (>= 7.0) activesupport (>= 7.0) @@ -149,7 +149,6 @@ GEM capybara-screenshot (1.0.26) capybara (>= 1.0, < 4) launchy - cgi (0.5.2) childprocess (5.0.0) coderay (1.1.3) coercible (1.0.0) @@ -322,8 +321,8 @@ GEM iniparse (~> 1.4) rexml (~> 3.2) pagy (9.4.0) - paper_trail (15.2.0) - activerecord (>= 6.1) + paper_trail (17.0.0) + activerecord (>= 7.1) request_store (~> 1.4) paper_trail-globalid (0.2.0) globalid @@ -373,20 +372,20 @@ GEM rack (>= 1.3) rackup (2.3.1) rack (>= 3) - rails (7.2.3.2) - actioncable (= 7.2.3.2) - actionmailbox (= 7.2.3.2) - actionmailer (= 7.2.3.2) - actionpack (= 7.2.3.2) - actiontext (= 7.2.3.2) - actionview (= 7.2.3.2) - activejob (= 7.2.3.2) - activemodel (= 7.2.3.2) - activerecord (= 7.2.3.2) - activestorage (= 7.2.3.2) - activesupport (= 7.2.3.2) + rails (8.1.3.1) + actioncable (= 8.1.3.1) + actionmailbox (= 8.1.3.1) + actionmailer (= 8.1.3.1) + actionpack (= 8.1.3.1) + actiontext (= 8.1.3.1) + actionview (= 8.1.3.1) + activejob (= 8.1.3.1) + activemodel (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) bundler (>= 1.15.0) - railties (= 7.2.3.2) + railties (= 8.1.3.1) rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest @@ -401,10 +400,9 @@ GEM nested_form (~> 0.3) rails (>= 6.0, < 9) turbo-rails (>= 1.0, < 3) - railties (7.2.3.2) - actionpack (= 7.2.3.2) - activesupport (= 7.2.3.2) - cgi + railties (8.1.3.1) + actionpack (= 8.1.3.1) + activesupport (= 8.1.3.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -595,7 +593,6 @@ DEPENDENCIES capybara capybara-lockstep capybara-screenshot - connection_pool (~> 2.5) cssbundling-rails devise devise_two_factor_authentication @@ -615,7 +612,7 @@ DEPENDENCIES nokogiri (~> 1.19.1) notifications-ruby-client overcommit (>= 0.37.0) - paper_trail (~> 15.2) + paper_trail (~> 17.0) paper_trail-globalid parallel_tests pg (~> 1.1) @@ -627,7 +624,7 @@ DEPENDENCIES rack (~> 3.1.20) rack-attack rack-mini-profiler (~> 3.3.0) - rails (~> 7.2.2) + rails (~> 8.1.3) rails_admin (~> 3.1) redcarpet (~> 3.6) redis (~> 4.8) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index c8ad4cb12..250d7b548 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -11,7 +11,7 @@ class UsersController < ApplicationController before_action -> { filter_manager.serialize_filters_to_session }, if: :current_user, only: %i[index] def index - redirect_to users_organisation_path(current_user.organisation) unless current_user.support? + return redirect_to users_organisation_path(current_user.organisation) unless current_user.support? || request.format.csv? all_users = User.visible.sorted_by_organisation_and_role filtered_users = filter_manager.filtered_users(all_users, search_term, session_filters) diff --git a/app/models/download_record.rb b/app/models/download_record.rb index 09c8ee851..497fa7062 100644 --- a/app/models/download_record.rb +++ b/app/models/download_record.rb @@ -11,8 +11,8 @@ class DownloadRecord < ApplicationRecord scheme_location: 3, }.freeze - enum download_type: DOWNLOAD_TYPE - enum user_role: User::ROLES + enum :download_type, DOWNLOAD_TYPE + enum :user_role, User::ROLES def self.build_from_user(user:, **attrs) new( diff --git a/app/models/location.rb b/app/models/location.rb index 4d9084f8c..af33f8e7e 100644 --- a/app/models/location.rb +++ b/app/models/location.rb @@ -172,7 +172,7 @@ class Location < ApplicationRecord LOCAL_AUTHORITIES = LocalAuthority.all.map { |la| [la.name, la.code] }.to_h attribute :local_authorities, :string - enum :local_authorities, LOCAL_AUTHORITIES + enum :local_authorities, LOCAL_AUTHORITIES if LOCAL_AUTHORITIES.present? def self.local_authorities_for_current_year LocalAuthority.all.active(Time.zone.today).england.map { |la| [la.code, la.name] }.to_h end diff --git a/app/models/log.rb b/app/models/log.rb index abb652474..3ad9edea0 100644 --- a/app/models/log.rb +++ b/app/models/log.rb @@ -50,7 +50,6 @@ class Log < ApplicationRecord scope :has_old_form_id, -> { where.not(old_form_id: nil) } scope :imported_2023_with_old_form_id, -> { imported.filter_by_year(2023).has_old_form_id } scope :imported_2023, -> { imported.filter_by_year(2023) } - # TODO: CLDC-4273: use .union in filter_by_organisation rather than raw SQL scope :filter_by_organisation, lambda { |orgs, _user = nil| owned = unscoped { where(owning_organisation: orgs).select(:id) } managed = unscoped { where(managing_organisation: orgs).select(:id) } diff --git a/app/views/locations/deactivate_confirm.html.erb b/app/views/locations/deactivate_confirm.html.erb index 6748af918..d1966e0ec 100644 --- a/app/views/locations/deactivate_confirm.html.erb +++ b/app/views/locations/deactivate_confirm.html.erb @@ -1,4 +1,4 @@ -<%= form_with model: @location_deactivation_period, url: scheme_location_deactivate_path(@location), method: "patch", local: true do |f| %> +<%= form_with url: scheme_location_deactivate_path(@location), method: "patch", local: true do |f| %> <% content_for :before_content do %> <%= govuk_back_link(href: :back) %> <% end %> diff --git a/app/views/schemes/deactivate_confirm.html.erb b/app/views/schemes/deactivate_confirm.html.erb index a73208e75..af0ced18e 100644 --- a/app/views/schemes/deactivate_confirm.html.erb +++ b/app/views/schemes/deactivate_confirm.html.erb @@ -1,4 +1,4 @@ -<%= form_with model: @scheme_deactivation_period, url: scheme_deactivate_path(@scheme), method: "patch", local: true do |f| %> +<%= form_with url: scheme_deactivate_path(@scheme), method: "patch", local: true do |f| %> <% content_for :before_content do %> <%= govuk_back_link(href: :back) %> <% end %> diff --git a/config/application.rb b/config/application.rb index cb8979a78..eab518c1f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -20,8 +20,8 @@ Bundler.require(*Rails.groups) module DataCollector class Application < Rails::Application - # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 7.2 + # Initialize configuration defaults for this Rails version. + config.load_defaults 8.1 # Please, add to the `ignore` list any other `lib` subdirectories that do # not contain `.rb` files, or that should not be reloaded or eager loaded. @@ -30,7 +30,7 @@ module DataCollector # it's strongly discouraged using add_autoload_paths_to_load_path, but rack_attack initializer can't load config files without it config.add_autoload_paths_to_load_path = true - + # Configuration for the application, engines, and railties goes here. # # These settings can be overridden in specific environments using the files diff --git a/config/initializers/devise_two_factor_authentication_rails8_patch.rb b/config/initializers/devise_two_factor_authentication_rails8_patch.rb new file mode 100644 index 000000000..ba06fa185 --- /dev/null +++ b/config/initializers/devise_two_factor_authentication_rails8_patch.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +# Compatibility patch for devise_two_factor_authentication 3.0.0 on Rails 8+. +# +# The gem's route mapper passes the non-standard :resend_code action into +# `resource ... only: [...]`. Rails 8 tightened `resource`/`resources` to raise +# an ArgumentError when :only/:except contain anything outside the standard REST +# actions. So, we redefine the mapper method here to drop it now we are on Rails 8+ +module ActionDispatch::Routing + class Mapper + protected + + def devise_two_factor_authentication(mapping, controllers) + resource :two_factor_authentication, + only: %i[show update], + path: mapping.path_names[:two_factor_authentication], + controller: controllers[:two_factor_authentication] do + collection { get resend_code_path(mapping), as: "resend_code" } + end + end + end +end diff --git a/spec/features/accessibility_spec.rb b/spec/features/accessibility_spec.rb index d039a70b5..d0ef930df 100644 --- a/spec/features/accessibility_spec.rb +++ b/spec/features/accessibility_spec.rb @@ -69,6 +69,7 @@ RSpec.describe "Accessibility", :js do context "when viewing organisation pages" do let(:parent_relationship) { create(:organisation_relationship, parent_organisation: other_user.organisation) } let(:child_relationship) { create(:organisation_relationship, child_organisation: other_user.organisation) } + let(:name_change) { create(:organisation_name_change, organisation: other_user.organisation) } let(:organisation_paths) do routes = find_routes("organisation", other_user.organisation, other_user.organisation).reject do |route| route.match?(/\A\/organisations\/#{other_user.organisation_id}\z/) || @@ -81,12 +82,15 @@ RSpec.describe "Accessibility", :js do "filters/update" => "?codes_only=false", "stock-owners/remove" => "?target_organisation_id=#{child_relationship.parent_organisation.id}", "managing-agents/remove" => "?target_organisation_id=#{parent_relationship.child_organisation.id}", + "organisations/search" => "?query=test", } routes.map do |route| additional_params = route_mappings.find { |pattern, _| route.include?(pattern) }&.last route += additional_params if additional_params - route + # find_routes does not substitute :change_id, so provide a real record id + # to visit the actual cancel-name-change page instead of an error page. + route.gsub(":change_id", name_change.id.to_s) end end