From d6cad9a35d10d38840fac31e615c418f0aa1793f Mon Sep 17 00:00:00 2001 From: Ana Botto Date: Tue, 30 Jan 2024 16:23:57 +0000 Subject: [PATCH] Add feature toggle for add downtime transition --- app/controllers/downtimes_controller.rb | 28 +-- .../legacy_downtimes_controller.rb | 75 ++++++++ app/views/legacy_downtimes/_form.html.erb | 88 +++++++++ .../edit.html.erb | 0 .../index.html.erb | 0 app/views/legacy_downtimes/new.html.erb | 18 ++ config/features.rb | 4 + config/routes.rb | 7 +- test/functional/downtimes_controller_test.rb | 72 +------- .../legacy_downtimes_controller_test.rb | 171 ++++++++++++++++++ 10 files changed, 364 insertions(+), 99 deletions(-) create mode 100644 app/controllers/legacy_downtimes_controller.rb create mode 100644 app/views/legacy_downtimes/_form.html.erb rename app/views/{downtimes => legacy_downtimes}/edit.html.erb (100%) rename app/views/{downtimes => legacy_downtimes}/index.html.erb (100%) create mode 100644 app/views/legacy_downtimes/new.html.erb create mode 100644 test/functional/legacy_downtimes_controller_test.rb diff --git a/app/controllers/downtimes_controller.rb b/app/controllers/downtimes_controller.rb index f7c9fc5ef..99b8b27c0 100644 --- a/app/controllers/downtimes_controller.rb +++ b/app/controllers/downtimes_controller.rb @@ -1,11 +1,7 @@ class DowntimesController < ApplicationController before_action :require_govuk_editor - before_action :load_edition, except: [:index] - before_action :process_params, only: %i[create update] - - def index - @transactions = TransactionEdition.published.order_by(%i[title asc]) - end + before_action :load_edition + before_action :process_params, only: %i[create] def new @downtime = Downtime.new(artefact: @edition.artefact) @@ -22,26 +18,6 @@ def create end end - def edit - @downtime = Downtime.for(@edition.artefact) - end - - def update - @downtime = Downtime.for(@edition.artefact) - - if params["commit"] == "Cancel downtime" - DowntimeRemover.destroy_immediately(@downtime) - flash[:success] = "#{edition_link} downtime message cancelled".html_safe - redirect_to downtimes_path - elsif @downtime.update(downtime_params) - DowntimeScheduler.schedule_publish_and_expiry(@downtime) - flash[:success] = "#{edition_link} downtime message re-scheduled (from #{view_context.downtime_datetime(@downtime)})".html_safe - redirect_to downtimes_path - else - render :edit - end - end - private def downtime_params diff --git a/app/controllers/legacy_downtimes_controller.rb b/app/controllers/legacy_downtimes_controller.rb new file mode 100644 index 000000000..72b1cf220 --- /dev/null +++ b/app/controllers/legacy_downtimes_controller.rb @@ -0,0 +1,75 @@ +class LegacyDowntimesController < ApplicationController + before_action :require_govuk_editor + before_action :load_edition, except: [:index] + before_action :process_params, only: %i[create update] + + def index + @transactions = TransactionEdition.published.order_by(%i[title asc]) + end + + def new + @downtime = Downtime.new(artefact: @edition.artefact) + end + + def create + @downtime = Downtime.new(downtime_params) + if @downtime.save + DowntimeScheduler.schedule_publish_and_expiry(@downtime) + flash[:success] = "#{edition_link} downtime message scheduled (from #{view_context.downtime_datetime(@downtime)})".html_safe + redirect_to downtimes_path + else + render :new + end + end + + def edit + @downtime = Downtime.for(@edition.artefact) + end + + def update + @downtime = Downtime.for(@edition.artefact) + + if params["commit"] == "Cancel downtime" + DowntimeRemover.destroy_immediately(@downtime) + flash[:success] = "#{edition_link} downtime message cancelled".html_safe + redirect_to downtimes_path + elsif @downtime.update(downtime_params) + DowntimeScheduler.schedule_publish_and_expiry(@downtime) + flash[:success] = "#{edition_link} downtime message re-scheduled (from #{view_context.downtime_datetime(@downtime)})".html_safe + redirect_to downtimes_path + else + render :edit + end + end + +private + + def downtime_params + params[:downtime].permit([ + "artefact_id", + "message", + "end_time(1i)", + "end_time(2i)", + "end_time(3i)", + "end_time(4i)", + "end_time(5i)", + "start_time(1i)", + "start_time(2i)", + "start_time(3i)", + "start_time(4i)", + "start_time(5i)", + ]) + end + + def load_edition + @edition = Edition.find(params[:edition_id]) + end + + def process_params + squash_multiparameter_datetime_attributes(downtime_params, %w[start_time end_time]) + end + + def edition_link + view_context.link_to(@edition.title, edit_edition_downtime_path(@edition), class: "link-inherit bold") + end +end diff --git a/app/views/legacy_downtimes/_form.html.erb b/app/views/legacy_downtimes/_form.html.erb new file mode 100644 index 000000000..b64bfc91d --- /dev/null +++ b/app/views/legacy_downtimes/_form.html.erb @@ -0,0 +1,88 @@ +<%= f.hidden_field :artefact_id %> + +<%= render :partial => 'shared/error_summary', locals: { object: @downtime} %> + +
+
+

This service will be unavailable from

+
+ +
+
+ <%= f.label :start_time, "Hour", for: "downtime_start_time_4i" %> + <%= select_hour @downtime.start_time&.hour || 1.hour.from_now.beginning_of_hour, { field_name: "start_time(4i)", prefix: "downtime" }, { class: "form-control date" } %> +
+ +
+ <%= f.label :start_time, "Minute", for: "downtime_start_time_5i" %> + <%= select_minute @downtime.start_time&.minute, { field_name: "start_time(5i)", prefix: "downtime" }, { class: "form-control date" } %> +
+ +
+

on

+
+ +
+ <%= f.label :start_time, "Day", for: "downtime_start_time_3i" %> + <%= select_day @downtime.start_time&.day || Date.tomorrow.day, { field_name: "start_time(3i)", prefix: "downtime" }, { class: "form-control date" } %> +
+
+ <%= f.label :start_time, "Month", for: "downtime_start_time_2i" %> + <%= select_month@downtime.start_time&.month || Date.tomorrow.month, { field_name: "start_time(2i)", prefix: "downtime" }, { class: "form-control date" } %> +
+ +
+ <%= f.label :start_time, "Year", for: "downtime_start_time_1i" %> + <%= select_year @downtime.start_time&.year || Date.tomorrow.year, { field_name: "start_time(1i)", prefix: "downtime" }, { class: "form-control date" } %> +
+
+ +
+

to

+
+ +
+ +
+ <%= f.label :end_time, "Hour", for: "downtime_end_time_4i" %> + <%= select_hour @downtime.end_time&.hour || 1.hour.from_now.beginning_of_hour, { field_name: "end_time(4i)", prefix: "downtime" }, { class: "form-control date" } %> +
+ +
+ <%= f.label :end_time, "Minute", for: "downtime_end_time_5i" %> + <%= select_minute @downtime.end_time&.minute, { field_name: "end_time(5i)", prefix: "downtime" }, { class: "form-control date" } %> +
+ +
+

on

+
+ +
+ <%= f.label :start_time, "Day", for: "downtime_end_time_3i" %> + <%= select_day @downtime.end_time&.day || Date.tomorrow.day, { field_name: "end_time(3i)", prefix: "downtime" }, { class: "form-control date" } %> +
+ +
+ <%= f.label :end_time, "Month", for: "downtime_end_time_2i" %> + <%= select_month @downtime.end_time&.month || Date.tomorrow.month, { field_name: "end_time(2i)", prefix: "downtime" }, { class: "form-control date" } %> +
+ +
+ <%= f.label :end_time, "Year", for: "downtime_end_time_1i" %> + <%= select_year @downtime.end_time&.year || Date.tomorrow.year, { field_name: "end_time(1i)", prefix: "downtime" }, { class: "form-control date" } %> +
+
+
+ +
+ +<%= form_group(f, :message) do %> + <%= f.text_area :message, class: 'form-control input-md-6 js-downtime-message', rows: 5 %> +<% end %> + +

+ + Messages appear on the start page one day before the downtime is due. +

+ +
diff --git a/app/views/downtimes/edit.html.erb b/app/views/legacy_downtimes/edit.html.erb similarity index 100% rename from app/views/downtimes/edit.html.erb rename to app/views/legacy_downtimes/edit.html.erb diff --git a/app/views/downtimes/index.html.erb b/app/views/legacy_downtimes/index.html.erb similarity index 100% rename from app/views/downtimes/index.html.erb rename to app/views/legacy_downtimes/index.html.erb diff --git a/app/views/legacy_downtimes/new.html.erb b/app/views/legacy_downtimes/new.html.erb new file mode 100644 index 000000000..e7e64545c --- /dev/null +++ b/app/views/legacy_downtimes/new.html.erb @@ -0,0 +1,18 @@ +<% content_for :page_title, 'Schedule downtime' %> + + + +
+

+ <%= @downtime.artefact.name %> + Schedule a downtime message +

+
+ +<%= form_for @downtime, url: edition_downtime_path(@edition), html: { class: 'form well remove-top-margin', 'data-module' => 'downtime-message' } do |f| %> + <%= render 'form', f: f %> + <%= f.submit 'Schedule downtime message', class: 'js-submit btn btn-success' %> +<% end %> diff --git a/config/features.rb b/config/features.rb index e023058ef..52fc88a34 100644 --- a/config/features.rb +++ b/config/features.rb @@ -12,4 +12,8 @@ feature :design_system_reports_page, default: false, description: "A transition of the reports page to use the GOV.UK Design System" + + feature :design_system_downtime_new, + default: false, + description: "A transition of the add downtime page to use the GOV.UK Design System" end diff --git a/config/routes.rb b/config/routes.rb index af094be48..e7dd1626f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,7 +12,7 @@ put "resolve", on: :member end - get "downtimes" => "downtimes#index" + get "downtimes" => "legacy_downtimes#index" resources :artefacts, only: %i[new create update] @@ -42,7 +42,10 @@ } end - resource :downtime, only: %i[new create edit update destroy] + constraints FeatureConstraint.new("design_system_downtime_new") do + resource :downtime, only: %i[new create] + end + resource :downtime, only: %i[new create edit update destroy], controller: "legacy_downtimes" end constraints FeatureConstraint.new("design_system_reports_page") do diff --git a/test/functional/downtimes_controller_test.rb b/test/functional/downtimes_controller_test.rb index 0ea525940..745d5459d 100644 --- a/test/functional/downtimes_controller_test.rb +++ b/test/functional/downtimes_controller_test.rb @@ -5,31 +5,6 @@ class DowntimesControllerTest < ActionController::TestCase login_as_stub_user end - context "#index" do - should "list all published transaction editions" do - unpublished_transaction_edition = FactoryBot.create(:transaction_edition) - transaction_editions = FactoryBot.create_list(:transaction_edition, 2, :published) - - get :index - - assert_response :ok - assert_select "h3.publication-table-title", count: 0, text: unpublished_transaction_edition.title - transaction_editions.each do |edition| - assert_select "h3.publication-table-title", text: edition.title - end - end - - should "redirect to root page if welsh_editor" do - login_as_welsh_editor - - get :index - - assert_response :redirect - assert_redirected_to controller: "root", action: "index" - assert_includes flash[:danger], "do not have permission" - end - end - context "#new" do should "render the page ok" do get :new, params: { edition_id: edition.id } @@ -53,7 +28,7 @@ class DowntimesControllerTest < ActionController::TestCase should "redirect to the downtime index page" do DowntimeScheduler.stubs(:schedule_publish_and_expiry) post :create, params: { edition_id: edition.id, downtime: downtime_params } - assert_redirected_to controller: "downtimes", action: "index" + assert_redirected_to controller: "legacy_downtimes", action: "index" end end @@ -75,51 +50,6 @@ class DowntimesControllerTest < ActionController::TestCase end end - context "#edit" do - should "render the page ok" do - create_downtime - get :edit, params: { edition_id: edition.id } - assert_response :ok - end - end - - context "#update" do - context "cancelling scheduled downtime" do - should "invoke the DowntimeRemover" do - DowntimeRemover.expects(:destroy_immediately).with(downtime) - put :update, params: { edition_id: edition.id, downtime: downtime_params, commit: "Cancel downtime" } - end - - should "redirect to the downtime index" do - DowntimeRemover.stubs(:destroy_immediately) - put :update, params: { edition_id: edition.id, downtime: downtime_params, commit: "Cancel downtime" } - assert_redirected_to controller: "downtimes", action: "index" - end - end - - context "rescheduling planned downtime" do - should "schedule the changes for publication and expiration" do - DowntimeScheduler.expects(:schedule_publish_and_expiry).with(downtime) - put :update, params: { edition_id: edition.id, downtime: downtime_params, commit: "Re-schedule downtime message" } - end - - should "redirect to the downtime index" do - create_downtime - DowntimeScheduler.stubs(:schedule_publish_and_expiry) - put :update, params: { edition_id: edition.id, downtime: downtime_params, commit: "Re-schedule downtime message" } - assert_redirected_to controller: "downtimes", action: "index" - end - end - - context "with invalid form data" do - should "rerender the page" do - create_downtime - put :update, params: { edition_id: edition.id, downtime: invalid_params, commit: "Re-schedule downtime message" } - assert_template :edit - end - end - end - def edition @edition ||= FactoryBot.create(:transaction_edition) end diff --git a/test/functional/legacy_downtimes_controller_test.rb b/test/functional/legacy_downtimes_controller_test.rb new file mode 100644 index 000000000..b1a751f4a --- /dev/null +++ b/test/functional/legacy_downtimes_controller_test.rb @@ -0,0 +1,171 @@ +require "test_helper" + +class LegacyDowntimesControllerTest < ActionController::TestCase + setup do + login_as_stub_user + end + + context "#index" do + should "list all published transaction editions" do + unpublished_transaction_edition = FactoryBot.create(:transaction_edition) + transaction_editions = FactoryBot.create_list(:transaction_edition, 2, :published) + + get :index + + assert_response :ok + assert_select "h3.publication-table-title", count: 0, text: unpublished_transaction_edition.title + transaction_editions.each do |edition| + assert_select "h3.publication-table-title", text: edition.title + end + end + + should "redirect to root page if welsh_editor" do + login_as_welsh_editor + + get :index + + assert_response :redirect + assert_redirected_to controller: "root", action: "index" + assert_includes flash[:danger], "do not have permission" + end + end + + context "#new" do + should "render the page ok" do + get :new, params: { edition_id: edition.id } + assert_response :ok + end + end + + context "#create" do + context "with valid params" do + should "create a new downtime" do + DowntimeScheduler.stubs(:schedule_publish_and_expiry) + post :create, params: { edition_id: edition.id, downtime: downtime_params } + assert_a_downtime_is_created + end + + should "schedule the publication and expiration of the downtime" do + DowntimeScheduler.expects(:schedule_publish_and_expiry) + post :create, params: { edition_id: edition.id, downtime: downtime_params } + end + + should "redirect to the downtime index page" do + DowntimeScheduler.stubs(:schedule_publish_and_expiry) + post :create, params: { edition_id: edition.id, downtime: downtime_params } + assert_redirected_to controller: "legacy_downtimes", action: "index" + end + end + + context "with invalid params" do + should "not create a downtime" do + post :create, params: { edition_id: edition.id, downtime: invalid_params } + assert_that_no_downtime_exists + end + + should "not schedule publishing and expiration" do + DowntimeScheduler.expects(:schedule_publish_and_expiry).never + post :create, params: { edition_id: edition.id, downtime: invalid_params } + end + + should "rerender the page" do + post :create, params: { edition_id: edition.id, downtime: invalid_params } + assert_template :new + end + end + end + + context "#edit" do + should "render the page ok" do + create_downtime + get :edit, params: { edition_id: edition.id } + assert_response :ok + end + end + + context "#update" do + context "cancelling scheduled downtime" do + should "invoke the DowntimeRemover" do + DowntimeRemover.expects(:destroy_immediately).with(downtime) + put :update, params: { edition_id: edition.id, downtime: downtime_params, commit: "Cancel downtime" } + end + + should "redirect to the downtime index" do + DowntimeRemover.stubs(:destroy_immediately) + put :update, params: { edition_id: edition.id, downtime: downtime_params, commit: "Cancel downtime" } + assert_redirected_to controller: "legacy_downtimes", action: "index" + end + end + + context "rescheduling planned downtime" do + should "schedule the changes for publication and expiration" do + DowntimeScheduler.expects(:schedule_publish_and_expiry).with(downtime) + put :update, params: { edition_id: edition.id, downtime: downtime_params, commit: "Re-schedule downtime message" } + end + + should "redirect to the downtime index" do + create_downtime + DowntimeScheduler.stubs(:schedule_publish_and_expiry) + put :update, params: { edition_id: edition.id, downtime: downtime_params, commit: "Re-schedule downtime message" } + assert_redirected_to controller: "legacy_downtimes", action: "index" + end + end + + context "with invalid form data" do + should "rerender the page" do + create_downtime + put :update, params: { edition_id: edition.id, downtime: invalid_params, commit: "Re-schedule downtime message" } + assert_template :edit + end + end + end + + def edition + @edition ||= FactoryBot.create(:transaction_edition) + end + + def downtime + @downtime ||= FactoryBot.create(:downtime, artefact: edition.artefact) + end + + def create_downtime + downtime + end + + def next_year + (Time.zone.now + 1.year).year + end + + def last_year + (Time.zone.now - 1.year).year + end + + def downtime_params + { + 'artefact_id': edition.artefact.id, + 'start_time(4i)': 11, + 'start_time(5i)': 0, + 'start_time(3i)': 14, + 'start_time(2i)': 3, + 'start_time(1i)': next_year, + 'end_time(4i)': 15, + 'end_time(5i)': 0, + 'end_time(3i)': 14, + 'end_time(2i)': 3, + 'end_time(1i)': next_year, + 'message': "foo", + } + end + + def invalid_params + downtime_params.merge('end_time(1i)': last_year) + end + + def assert_a_downtime_is_created + assert_equal 1, Downtime.count + end + + def assert_that_no_downtime_exists + assert_equal 0, Downtime.count + end +end