From 3cdf713e05a29409ee57503b35e2589133d43d18 Mon Sep 17 00:00:00 2001 From: Slawosz Slawinski Date: Thu, 15 Feb 2024 12:42:14 +0000 Subject: [PATCH] Allow registration when service is closed --- app/constraints/has_flipper_access.rb | 4 +- .../closed_registration_users_controller.rb | 21 +++++++ app/controllers/application_controller.rb | 6 ++ .../registration_wizard_controller.rb | 11 +++- app/models/admin.rb | 5 ++ app/models/closed_registration_user.rb | 2 + app/models/registration_wizard.rb | 1 - app/models/user.rb | 5 -- app/services/feature.rb | 8 +-- app/views/admin/_layout.html.erb | 4 ++ .../closed_registration_users/index.html.erb | 50 +++++++++++++++ .../closed_registration_users/new.html.erb | 13 ++++ .../closed_registration_exception.html.erb | 63 +++++++++++++++++++ config/routes.rb | 2 + ...174312_create_closed_registration_users.rb | 9 +++ db/schema.rb | 6 ++ spec/features/service_closed_spec.rb | 52 ++++++++++++++- spec/lib/services/feature_spec.rb | 12 ++-- spec/models/registration_wizard_spec.rb | 10 --- spec/rails_helper.rb | 4 ++ 20 files changed, 256 insertions(+), 32 deletions(-) create mode 100644 app/controllers/admin/closed_registration_users_controller.rb create mode 100644 app/models/closed_registration_user.rb create mode 100644 app/views/admin/closed_registration_users/index.html.erb create mode 100644 app/views/admin/closed_registration_users/new.html.erb create mode 100644 app/views/pages/closed_registration_exception.html.erb create mode 100644 db/migrate/20240215174312_create_closed_registration_users.rb diff --git a/app/constraints/has_flipper_access.rb b/app/constraints/has_flipper_access.rb index b099b43ddb..0c85a750d3 100644 --- a/app/constraints/has_flipper_access.rb +++ b/app/constraints/has_flipper_access.rb @@ -1,8 +1,8 @@ class HasFlipperAccess def self.matches?(request) - current_user_id = request.session["user_id"] + current_user_id = request.session["admin_id"] - current_user = User.find_by(id: current_user_id) + current_user = Admin.find_by(id: current_user_id) current_user.present? && current_user.flipper_access? end diff --git a/app/controllers/admin/closed_registration_users_controller.rb b/app/controllers/admin/closed_registration_users_controller.rb new file mode 100644 index 0000000000..2c2ee9c0ed --- /dev/null +++ b/app/controllers/admin/closed_registration_users_controller.rb @@ -0,0 +1,21 @@ +class Admin::ClosedRegistrationUsersController < SuperAdminController + def index + @users = ClosedRegistrationUser.all + @user = ClosedRegistrationUser.new + end + + def new + @user = ClosedRegistrationUser.new + end + + def create + @user = ClosedRegistrationUser.new(params[:closed_registration_user].permit(:email)) + if @user.save + flash[:success] = "New closed registration user created" + redirect_to admin_closed_registration_users_path + else + flash[:error] = "Can not create a user" + render :index + end + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f102036948..b7757a6ade 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,6 +2,7 @@ class ApplicationController < ActionController::Base default_form_builder GOVUKDesignSystemFormBuilder::FormBuilder before_action :set_sentry_user + before_action :set_feature_flag_users private @@ -41,4 +42,9 @@ def current_admin Admin.find_by(id: session[:admin_id]) end helper_method :current_admin + + def set_feature_flag_users + users = User.where(email: ClosedRegistrationUser.pluck(:email)) + users.each { |u| Flipper.enable_actor(Feature::REGISTRATION_OPEN, u) } + end end diff --git a/app/controllers/registration_wizard_controller.rb b/app/controllers/registration_wizard_controller.rb index 38ed0b2e5e..6a1dc59d99 100644 --- a/app/controllers/registration_wizard_controller.rb +++ b/app/controllers/registration_wizard_controller.rb @@ -1,4 +1,5 @@ class RegistrationWizardController < ApplicationController + before_action :registration_closed before_action :set_wizard before_action :set_form before_action :check_end_of_journey, only: %i[update] @@ -49,12 +50,20 @@ def check_end_of_journey end end + def registration_closed + return if request.path == registration_wizard_show_path(:closed) + + if Feature.registration_closed?(current_user) + redirect_to registration_wizard_show_path(:closed) + end + end + def store session["registration_store"] ||= {} end def wizard_params - return {} if Feature.registration_closed? + return {} if Feature.registration_closed?(current_user) params.fetch(:registration_wizard, {}).permit(RegistrationWizard.permitted_params_for_step(params[:step].underscore)) end diff --git a/app/models/admin.rb b/app/models/admin.rb index 5f1a2c1a60..66ad64b904 100644 --- a/app/models/admin.rb +++ b/app/models/admin.rb @@ -1,4 +1,9 @@ class Admin < ApplicationRecord validates :full_name, presence: true, length: { maximum: 64 } validates :email, presence: true, length: { maximum: 64 } + + # Whether this user has admin access to the feature flagging interface + def flipper_access? + super_admin? + end end diff --git a/app/models/closed_registration_user.rb b/app/models/closed_registration_user.rb new file mode 100644 index 0000000000..5f6af8496a --- /dev/null +++ b/app/models/closed_registration_user.rb @@ -0,0 +1,2 @@ +class ClosedRegistrationUser < ApplicationRecord +end diff --git a/app/models/registration_wizard.rb b/app/models/registration_wizard.rb index 6462ab8e2d..0637d3336f 100644 --- a/app/models/registration_wizard.rb +++ b/app/models/registration_wizard.rb @@ -59,7 +59,6 @@ class InvalidStep < StandardError; end attr_reader :current_step, :params, :store, :request, :current_user def initialize(current_step:, store:, request:, current_user:, params: {}) - current_step = :closed if Feature.registration_closed? set_current_step(current_step) @current_user = current_user diff --git a/app/models/user.rb b/app/models/user.rb index db82c4a6a2..a7a5680d44 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -129,11 +129,6 @@ def ecf_sync_jobs .order(run_at: :asc) end - # Whether this user has admin access to the feature flagging interface - def flipper_access? - admin? && super_admin? - end - def flipper_id "User;#{retrieve_or_persist_feature_flag_id}" end diff --git a/app/services/feature.rb b/app/services/feature.rb index 2486942e82..765a9ee2af 100644 --- a/app/services/feature.rb +++ b/app/services/feature.rb @@ -1,11 +1,11 @@ class Feature REGISTRATION_OPEN_DATE = Time.zone.parse("6 June 2022 12:00") - REGISTRATION_CLOSED_KEY = "Registration closed".freeze + REGISTRATION_OPEN = "Registration open".freeze REGISTRATION_DISABLED = "Registration disabled".freeze FEATURE_FLAG_KEYS = [ - REGISTRATION_CLOSED_KEY, + REGISTRATION_OPEN, ].freeze class << self @@ -27,8 +27,8 @@ def trn_required? true end - def registration_closed? - Flipper.enabled?(REGISTRATION_CLOSED_KEY) + def registration_closed?(user) + !Flipper.enabled?(REGISTRATION_OPEN, user) end def registration_disabled? diff --git a/app/views/admin/_layout.html.erb b/app/views/admin/_layout.html.erb index 8d8e6e31e5..29ec977d9d 100644 --- a/app/views/admin/_layout.html.erb +++ b/app/views/admin/_layout.html.erb @@ -49,5 +49,9 @@ <% component.with_nav_item(path: admin_settings_path) do %> <%= t(".settings") %> <% end %> + + <% component.with_nav_item(path: "/admin/closed_registration_users") do %> + <%= "Closed registration users" %> + <% end %> <% end %> <% end %> diff --git a/app/views/admin/closed_registration_users/index.html.erb b/app/views/admin/closed_registration_users/index.html.erb new file mode 100644 index 0000000000..9708e1480d --- /dev/null +++ b/app/views/admin/closed_registration_users/index.html.erb @@ -0,0 +1,50 @@ +
+
+ <%= render "admin/layout", title: "Closed registration users" %> + +

Add new email

+ <%= form_for [:admin, @user] do |f| %> + <%= f.govuk_error_summary %> + <%= f.govuk_text_field :email, legend: { text: 'Provide course start date' } %> + <%= f.govuk_submit "Save" %> + <% end %> + +

Currently allowed emails

+ + + + + + + + + + + + <% @users.each do |user| %> + + + + + + <% end %> + +
UserHas accountLast application date
<%= user.email %> + <% service_user = User.find_by(email: user.email) %> + <% if service_user.present? %> + + Yes + + <% else %> + + No + + <% end %> + + <% if service_user.present? %> + <%= service_user.applications.order("created_at DESC").first.try(:created_at) %> + <% end %> +
+ +
+
diff --git a/app/views/admin/closed_registration_users/new.html.erb b/app/views/admin/closed_registration_users/new.html.erb new file mode 100644 index 0000000000..0a48b121a1 --- /dev/null +++ b/app/views/admin/closed_registration_users/new.html.erb @@ -0,0 +1,13 @@ +

Admin::LateRegistrants#new

+

Find me in app/views/admin/late_registrants/new.html.erb

+ +
+
+ <%= render "admin/layout", title: "Settings" %> + <%= form_for [:admin, @user] do |f| %> + <%= f.govuk_error_summary %> + <%= f.govuk_text_field :email, legend: { text: 'Provide course start date' } %> + <%= f.govuk_submit "Save" %> + <% end %> +
+
diff --git a/app/views/pages/closed_registration_exception.html.erb b/app/views/pages/closed_registration_exception.html.erb new file mode 100644 index 0000000000..f5877dcbb5 --- /dev/null +++ b/app/views/pages/closed_registration_exception.html.erb @@ -0,0 +1,63 @@ +<%= render "pages/choose_an_npq_and_provider_content" %> +
+
+

Register for a national professional qualification (NPQ)

+ +

+ You need to know which NPQ you want to do and who your provider is. If you have not chosen + yet, <%= govuk_link_to("find out about NPQ courses", "https://www.gov.uk/guidance/national-professional-qualification-npq-courses") %>. + +

+ +
+
+ Exceptional access to NPQ reqistration - contact support first +
+
+ +

Use this service to:

+ +
    +
  • register for an NPQ or <%= localise_sentence_embedded_course_name(Course.ehco) %> starting before <%= application_course_start_date %>
  • +
  • check your registration details, if you already registered
  • +
  • check your course outcome, if you have completed your course
  • +
+ +

You also need to apply separately with your training provider. They’ll send you an application form by email once you’ve registered.

+ +

Before you start

+ + +

+ You need to know which NPQ you want to do and who your provider is. If you have not chosen + yet, <%= govuk_link_to("find out about NPQ courses", "https://www.gov.uk/guidance/national-professional-qualification-npq-courses") %>. + +

+ +

+ You also need a teacher reference number (TRN) to register for an NPQ, even if you’re not a + teacher. <%= govuk_link_to("Learn about TRNs", "https://www.gov.uk/guidance/teacher-reference-number-trn") %> + including how to request one if you do not have one. +

+ +

+ If you work in early years or + childcare, <%= govuk_link_to("check if your workplace is registered with Ofsted", "https://reports.ofsted.gov.uk/childcare") %> + and get their Ofsted unique reference number (URN) if they have one. +

+ +

+ Before registering for an NPQ you’ll be asked to create a DfE Identity account or sign in to your account. +

+ + <% if Feature.registration_enabled? %> + <%= + render( + 'shared/get_an_identity/redirect_button', + button_text: "Start now" + ) + %> + <% end %> + +
+
diff --git a/config/routes.rb b/config/routes.rb index f3f6c7841d..1414dff2ec 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -35,6 +35,7 @@ get "/privacy-policy", to: redirect("https://www.gov.uk/government/publications/privacy-information-education-providers-workforce-including-teachers/privacy-information-education-providers-workforce-including-teachers#NPQ"), as: :privacy_policy get "/accessibility-statement", to: "pages#show", page: "accessibility" get "/choose-an-npq-and-provider", to: "pages#show", page: "choose_an_npq_and_provider" + get "/closed_registration_exception", to: "pages#show", page: "closed_registration_exception" resource :cookie_preferences do member do @@ -72,6 +73,7 @@ end resources "settings" + resources :closed_registration_users end get "/admin", to: "admin#show" diff --git a/db/migrate/20240215174312_create_closed_registration_users.rb b/db/migrate/20240215174312_create_closed_registration_users.rb new file mode 100644 index 0000000000..71bb21e1e5 --- /dev/null +++ b/db/migrate/20240215174312_create_closed_registration_users.rb @@ -0,0 +1,9 @@ +class CreateClosedRegistrationUsers < ActiveRecord::Migration[7.1] + def change + create_table :closed_registration_users do |t| + t.string :email + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 08f5adc1e5..9e5680a402 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -89,6 +89,12 @@ t.index ["user_id"], name: "index_applications_on_user_id" end + create_table "closed_registration_users", force: :cascade do |t| + t.string "email" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "cohorts", force: :cascade do |t| t.integer "start_year", null: false t.datetime "registration_start_date", null: false diff --git a/spec/features/service_closed_spec.rb b/spec/features/service_closed_spec.rb index e7c4da3d22..fa93a2316c 100644 --- a/spec/features/service_closed_spec.rb +++ b/spec/features/service_closed_spec.rb @@ -1,7 +1,9 @@ require "rails_helper" RSpec.feature "Service is hard closed", type: :feature do - include_context "Stub Get An Identity Omniauth Responses" + include Helpers::AdminLogin + include Helpers::JourneyAssertionHelper + include Helpers::JourneyStepHelper scenario "Service close date has passed" do close_registration! @@ -32,13 +34,57 @@ expect(page).to have_content("Registration for NPQs has closed temporarily") end + context "when service is closed" do + include_context "Stub Get An Identity Omniauth Responses" + + let(:super_admin) { create(:super_admin) } + let(:email) { "example@example.com" } + let(:user_email) { email } + + before { close_registration! } + + scenario "Allow user to register" do + visit "/" + expect(page).to have_content("Registration for NPQs has closed temporarily") + + sign_in_as(super_admin) + + click_link("Closed registration user") + fill_in("Email", with: email) + click_on("Save") + + expect(page).to have_content("New closed registration user created") + + click_link("Sign out") + + visit "/closed_registration_exception" + + click_on("Start now") + + expect_page_to_have(path: "/registration/course-start-date", submit_form: true) do + expect(page).to have_text("NPQ start dates are usually every February and October.") + end + end + + scenario "When user is not whitelisted" do + visit "/" + expect(page).to have_content("Registration for NPQs has closed temporarily") + + visit "/closed_registration_exception" + + click_on("Start now") + + expect_page_to_have(path: "/registration/closed") + end + end + private def close_registration! - Flipper.enable(Feature::REGISTRATION_CLOSED_KEY) + Flipper.disable(Feature::REGISTRATION_OPEN) end def open_registration! - Flipper.disable(Feature::REGISTRATION_CLOSED_KEY) + Flipper.enable(Feature::REGISTRATION_OPEN) end end diff --git a/spec/lib/services/feature_spec.rb b/spec/lib/services/feature_spec.rb index c8bb01b880..01565b7c06 100644 --- a/spec/lib/services/feature_spec.rb +++ b/spec/lib/services/feature_spec.rb @@ -2,23 +2,23 @@ RSpec.describe Feature do describe "#registration_closed?" do - context 'with Flipper flag "Registration closed" turned on' do + context 'with Flipper flag "Registration open" turned on' do before do - Flipper.enable(Feature::REGISTRATION_CLOSED_KEY) + Flipper.enable(Feature::REGISTRATION_OPEN) end it "returns true" do - expect(described_class.registration_closed?).to eq true + expect(described_class.registration_closed?(nil)).to eq false end end - context 'with Flipper flag "Registration closed" turned off' do + context 'with Flipper flag "Registration open" turned off' do before do - Flipper.disable(Feature::REGISTRATION_CLOSED_KEY) + Flipper.disable(Feature::REGISTRATION_OPEN) end it "returns false" do - expect(described_class.registration_closed?).to eq false + expect(described_class.registration_closed?(nil)).to eq true end end end diff --git a/spec/models/registration_wizard_spec.rb b/spec/models/registration_wizard_spec.rb index 87ab47bbc3..065c7dc00e 100644 --- a/spec/models/registration_wizard_spec.rb +++ b/spec/models/registration_wizard_spec.rb @@ -23,16 +23,6 @@ }.to raise_error(RegistrationWizard::InvalidStep) end end - - context "when registration is closed" do - before do - Flipper.enable(Feature::REGISTRATION_CLOSED_KEY) - end - - it "always returns closed" do - expect(subject.current_step).to be(:closed) - end - end end describe "#answers" do diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 9a7e07b30e..0f8ae4cb4c 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -113,6 +113,10 @@ # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") + config.before do + Flipper.enable(Feature::REGISTRATION_OPEN) + end + config.before(:each, exceptions_app: true) do # Make the app behave how it does in non dev/test environments and use the # ErrorsController via config.exceptions_app = routes in config/application.rb