Skip to content

Commit

Permalink
Merge pull request #2130 from unboxed/consultee-expiredlink
Browse files Browse the repository at this point in the history
Resend magic link for consultees
  • Loading branch information
benbaumann95 authored Jan 24, 2025
2 parents 50c64f4 + 6e9d392 commit 8435a62
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,28 @@

module BopsConsultees
class PlanningApplicationsController < ApplicationController
before_action :authenticate_with_sgid!
before_action :set_planning_application, only: :show
before_action :authenticate_with_sgid!, only: :show
before_action :set_planning_application, only: %i[show resend_link]
before_action :set_consultee, only: :resend_link

def show
respond_to do |format|
format.html
end
end

def resend_link
BopsCore::MagicLinkMailerJob.perform_later(
resource: @consultee,
subdomain: @current_local_authority.subdomain,
planning_application: @planning_application.presented
)

respond_to do |format|
format.html { redirect_to root_url, notice: "A magic link has been sent to: #{@consultee.email_address}" }
end
end

private

def set_planning_application
Expand All @@ -20,6 +33,13 @@ def set_planning_application
render_not_found
end

def set_consultee
expired_resource = BopsCore::SgidAuthenticationService.new(sgid).expired_resource
@consultee = @planning_application.consultation.consultees.find(expired_resource.id)
rescue ActiveRecord::RecordNotFound
render_not_found
end

def planning_applications_scope
@current_local_authority.planning_applications
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

module BopsConsultees
class PlanningApplicationPresenter
include Presentable
include BopsCore::Presentable

presents :planning_application

include BopsCore::StatusPresenter
include ActionView::Helpers::SanitizeHelper

def initialize(template, planning_application)
@template = template
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-full">
<h1 class="govuk-heading-l">
Consultees
BOPS Consultees
</h1>

<% if @resend_link %>
<p class="govuk-body">Your magic link has expired. Click resend to generate another link.</p>
<% if @expired_resource %>
<p class="govuk-body"><strong>Your magic link has expired</strong></p>

<p class="govuk-body">
Contact
<%= tag.a href: "mailto:#{current_local_authority.feedback_email}", class: "govuk-link" do %>
<% current_local_authority.feedback_email %>
<% end %>
if you think there's a problem.
</p>

<%= govuk_button_to "Send a new magic link", resend_link_planning_application_path(params[:reference], sgid: params[:sgid]) %>
<% end %>
</div>
</div>
4 changes: 3 additions & 1 deletion engines/bops_consultees/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
root to: redirect("dashboard")

resource :dashboard, only: %i[show]
resources :planning_applications, param: :reference, only: %i[show]
resources :planning_applications, param: :reference, only: %i[show] do
post :resend_link, on: :member
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,25 @@

context "with expired magic link" do
let!(:sgid) { consultee.sgid(expires_in: 1.minute, for: "magic_link") }
let(:mail) { ActionMailer::Base.deliveries }

it "I can see that the link has expired" do
travel 2.minutes
visit "/consultees/planning_applications/#{reference}?sgid=#{sgid}"
expect(page).not_to have_content(planning_application.full_address)
expect(page).not_to have_content(reference)
expect(page).to have_content("Your magic link has expired. Click resend to generate another link.")
expect(page).to have_content("Your magic link has expired")
expect(page).to have_content("Contact #{local_authority.feedback_email} if you think there's a problem.")

delivered_emails = mail.count
click_button("Send a new magic link")
expect(page).to have_content("A magic link has been sent to: #{consultee.email_address}")
perform_enqueued_jobs
expect(mail.count).to eql(delivered_emails + 1)

url = mail.last.body.raw_source.match(/href="(?<url>.+?)">/)[:url]
visit url
expect(page).to have_content(reference)
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ def sgid
end

def handle_expired_or_invalid_sgid
if (resource = sgid_authentication_service.expired_resource)
@resend_link = resource.sgid
if (@expired_resource = sgid_authentication_service.expired_resource)
render_expired
else
render_not_found
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

module BopsCore
class ApplicationJob < ActiveJob::Base
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module BopsCore
class MagicLinkMailerJob < ApplicationJob
queue_as :low_priority

def perform(resource:, planning_application:, subdomain:)
MagicLinkMailer.magic_link_mail(
resource: resource,
planning_application: planning_application,
subdomain: subdomain
).deliver_now
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

module BopsCore
class MagicLinkMailer < ApplicationMailer
def magic_link_mail(resource:, subdomain:, subject: "Your magic link")
def magic_link_mail(resource:, subdomain:, planning_application:, subject: "Your BOPS magic link")
@resource = resource
@sgid = resource.sgid
@subdomain = subdomain
@planning_application = planning_application
@url = magic_link_url

mail(
Expand All @@ -16,12 +17,14 @@ def magic_link_mail(resource:, subdomain:, subject: "Your magic link")

private

attr_reader :resource, :sgid, :subdomain
attr_reader :resource, :sgid, :subdomain, :planning_application

def magic_link_url
case resource
when Consultee
bops_consultees.dashboard_url(sgid:, subdomain:)
bops_consultees.planning_application_url(
reference: planning_application.reference, sgid:, subdomain:
)
else
main_app.root_url
end
Expand Down
35 changes: 35 additions & 0 deletions engines/bops_core/app/presenters/bops_core/presentable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module BopsCore
module Presentable
extend ActiveSupport::Concern
include Rails.application.routes.url_helpers

attr_reader :template, :planning_application

delegate :tag, :concat, :link_to, :truncate, :link_to_if, to: :template
delegate :to_param, to: :presented

class_methods do
def presents(presented)
define_method(:presented) do
@presented ||= send(presented)
end
end
end

private

def method_missing(method_name, *, **kwargs, &)
if presented.respond_to?(method_name)
kwargs.any? ? presented.send(method_name, **kwargs, &) : presented.send(method_name, *, &)
else
super
end
end

def respond_to_missing?(method_name, *args)
presented.respond_to?(method_name) || super
end
end
end
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Hello <%= @resource.name %>

Click the link below to access your dashboard:
This is your magic link.

<%= link_to "Access Dashboard", @url %>
<%= link_to "View BOPS application: #{@planning_application.reference_in_full}", @url %>

This link will expire in 48 hours.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

require "bops_core_helper"

RSpec.describe BopsCore::MagicLinkMailerJob, type: :job do
let(:consultation) { create(:consultation) }
let(:consultee) { create(:consultee, consultation:) }
let(:planning_application) { create(:planning_application, consultation:) }

it "enqueues the job" do
expect {
BopsCore::MagicLinkMailerJob.perform_later(resource: consultee, planning_application:, subdomain: "southwark")
}.to have_enqueued_job(BopsCore::MagicLinkMailerJob).with(resource: consultee, planning_application:, subdomain: "southwark")
end

it "sends the email" do
expect {
perform_enqueued_jobs do
BopsCore::MagicLinkMailerJob.perform_now(resource: consultee, planning_application:, subdomain: "southwark")
end
}.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end
36 changes: 36 additions & 0 deletions engines/bops_core/spec/mailers/bops_core/magic_link_mailer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

require "bops_core_helper"

RSpec.describe BopsCore::MagicLinkMailer, type: :mailer do
describe "magic_link_mail" do
let(:consultation) { create(:consultation) }
let(:consultee) { create(:consultee, consultation:, email_address: "[email protected]", name: "Bops Consultee") }
let(:planning_application) { consultation.planning_application }
let(:mail) { described_class.magic_link_mail(resource: consultee, planning_application:, subdomain: "southwark", subject: "Your magic link") }

before do
allow(consultee).to receive(:sgid).and_return("123456789")
end

it "renders the subject" do
expect(mail.subject).to eq("Your magic link")
end

it "sends to the correct email address" do
expect(mail.to).to eq([consultee.email_address])
end

it "assigns the correct magic link URL" do
expect(mail.body.encoded).to include("http://southwark.bops.services/consultees/planning_applications/25-00100-LDCE?sgid=123456789")
end

it "includes the planning application reference in the email body" do
expect(mail.body.encoded).to include("View BOPS application: #{planning_application.reference_in_full}")
end

it "includes the link expiration information" do
expect(mail.body.encoded).to include("This link will expire in 48 hours.")
end
end
end

0 comments on commit 8435a62

Please sign in to comment.