Skip to content

Commit

Permalink
Merge pull request #22 from alphagov/emergency-banner
Browse files Browse the repository at this point in the history
Add Emergency Banner handling
  • Loading branch information
KludgeKML authored Dec 16, 2024
2 parents c69bee6 + f7e971e commit 82d8118
Show file tree
Hide file tree
Showing 18 changed files with 224 additions and 9 deletions.
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ PATH
remote: .
specs:
govuk_web_banners (0.2.0)
govuk_app_config
govuk_publishing_components
rails (>= 7)
redis

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -465,6 +467,10 @@ GEM
rake (13.2.1)
rdoc (6.8.1)
psych (>= 4.0.0)
redis (5.3.0)
redis-client (>= 0.22.0)
redis-client (0.22.2)
connection_pool
regexp_parser (2.9.3)
reline (0.5.12)
io-console (~> 0.5)
Expand Down
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Proof of Concept for centralising handling of Recruitment, Global, and Emergency
banners (currently spread across apps)

## Usage
Currently supports recruitment banners
Currently supports the emergency banner and recruitment banners.

## Adding the gem to your application
Add this line to your application's Gemfile:
Expand All @@ -28,6 +28,39 @@ Add the JS dependencies to your existing asset dependencies file:
//= require govuk_web_banners/dependencies
```

## Adding emergency banners

Emergency banners are passed to the [Layout for Public](https://components.publishing.service.gov.uk/component-guide/layout_for_public) component, which is currently applied to each frontend app by the slimmer/static wrapping code - so you will only need to handle emergency banners in your app when Slimmer is removed from it. Once Slimmer is removed and you are calling the layout_for_public component directly in your app, add the emergency banner partial to the component's `emergency_banner:` key:

```
<%= render "govuk_publishing_components/components/layout_for_public", {
draft_watermark: draft_environment,
emergency_banner: render("govuk_web_banners/emergency_banner"), # <-- Add this line
full_width: false,
...etc
```

if you want the homepage variant of the banner, you can add `homepage: true` to the render call:

```
<%= render "govuk_publishing_components/components/layout_for_public", {
draft_watermark: draft_environment,
emergency_banner: render("govuk_web_banners/emergency_banner", homepage: true), # <-- Add this line
full_width: full_width,
...etc
```

Your app will also need access to the whitehall shared redis cluster (which is used to signal the emergency banner is up), via the `EMERGENCY_BANNER_REDIS_URL` environment variable (here is an example of [setting this in govuk-helm-charts](https://github.com/alphagov/govuk-helm-charts/blob/7818eaa22fc194d21548f316bcc5a46c2023dcb6/charts/app-config/values-staging.yaml#L3337-L3338)). You'll need to allow this in all three environments.

Finally, you'll need to configure a connection to the redis cluster, available at `Rails.application.config.emergency_banner_redis_client`. The suggested way of doing this is creating an initializer at `/config/initializers/govuk_web_banners.rb` with the content:

```
Rails.application.config.emergency_banner_redis_client = Redis.new(
url: ENV["EMERGENCY_BANNER_REDIS_URL"],
reconnect_attempts: [15, 30, 45, 60],
)
```

## Adding recruitment banners

Add a call to the partial in the layout or view that you want banners to appear
Expand Down
11 changes: 11 additions & 0 deletions app/views/govuk_web_banners/_emergency_banner.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<% emergency_banner = GovukWebBanners::EmergencyBanner.new %>
<% if emergency_banner.active? %>
<%= render "govuk_publishing_components/components/emergency_banner", {
campaign_class: emergency_banner.campaign_class,
heading: emergency_banner.heading,
link: emergency_banner.link,
link_text: emergency_banner.link_text,
short_description: emergency_banner.short_description,
homepage: local_assigns[:homepage] || false,
} %>
<% end %>
2 changes: 2 additions & 0 deletions govuk_web_banners.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ Gem::Specification.new do |spec|

spec.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"].reject { |f| f.match(/validators/) }

spec.add_dependency "govuk_app_config"
spec.add_dependency "govuk_publishing_components"
spec.add_dependency "rails", ">= 7"
spec.add_dependency "redis"

spec.add_development_dependency "byebug"
spec.add_development_dependency "govuk_test"
Expand Down
1 change: 1 addition & 0 deletions lib/govuk_web_banners.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require "govuk_publishing_components"

require "govuk_web_banners/emergency_banner"
require "govuk_web_banners/engine"
require "govuk_web_banners/recruitment_banner"
require "govuk_web_banners/version"
Expand Down
37 changes: 37 additions & 0 deletions lib/govuk_web_banners/emergency_banner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
require "govuk_app_config/govuk_error"
require "redis"

module GovukWebBanners
class EmergencyBanner
attr_reader :campaign_class, :heading, :short_description, :link, :link_text

def initialize
content = content_from_redis

@campaign_class = content[:campaign_class].presence
@heading = content[:heading].presence
@short_description = content[:short_description].presence
@link = content[:link].presence
@link_text = content[:link_text].presence
end

def active?
[campaign_class, heading].all?
end

private

def redis_client
Rails.application.config.emergency_banner_redis_client
end

def content_from_redis
Rails.cache.fetch("#emergency_banner/config", expires_in: 1.minute) do
redis_client.hgetall("emergency_banner").try(:symbolize_keys)
end
rescue StandardError => e
GovukError.notify(e, extra: { context: "Emergency Banner Redis" })
{}
end
end
end
17 changes: 17 additions & 0 deletions spec/dummy/app/assets/config/manifest.js
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
//= link govuk_web_banners_manifest.js

// Everything below here is needed for the layout_for_public component in the system tests

//= link govuk_publishing_components/vendor/lux/lux-measurer.js
//= link govuk_publishing_components/vendor/lux/lux-reporter.js
//= link govuk_publishing_components/rum-loader.js
//= link govuk_publishing_components/load-analytics.js
//= link govuk_publishing_components/ie.js

//= link application.css
//= link application.js
//= link favicon.ico
//= link favicon.svg
//= link govuk-icon-mask.svg

//= link govuk-icon-180.png
//= link govuk-opengraph-image.png
1 change: 1 addition & 0 deletions spec/dummy/app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Empty - needed for the layout_for_public component in the system tests
1 change: 1 addition & 0 deletions spec/dummy/app/assets/stylesheets/application.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Empty - needed for the layout_for_public component in the system tests */
4 changes: 4 additions & 0 deletions spec/dummy/app/views/banner_pages/emergency.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<%= render "govuk_publishing_components/components/layout_for_public", {
emergency_banner: render("govuk_web_banners/emergency_banner")
}
%>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<%= render "govuk_publishing_components/components/layout_for_public", {
emergency_banner: render("govuk_web_banners/emergency_banner", homepage: true)
}
%>
1 change: 1 addition & 0 deletions spec/dummy/config/initializers/govuk_web_banners.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rails.application.config.emergency_banner_redis_client = Redis.new
7 changes: 5 additions & 2 deletions spec/dummy/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
mount GovukPublishingComponents::Engine, at: "/component-guide"
mount GovukWebBanners::Engine => "/govuk_web_banners"

get "/page-with-banners", to: "banner_pages#show"
get "/page-with-no-banners", to: "banner_pages#show"
get "/recruitment-with-banners", to: "banner_pages#recruitment"
get "/recruitment-with-no-banners", to: "banner_pages#recruitment"

get "/emergency", to: "banner_pages#emergency"
get "/", to: "banner_pages#emergency_on_homepage"
end
4 changes: 2 additions & 2 deletions spec/fixtures/requests_banners.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Fixture - elements for request tests with dummy app
banners:
- name: Banner 1
suggestion_text: "This banner appears at /pages-with-banners!"
suggestion_text: "This banner appears at /recruitment-with-banners!"
suggestion_link_text: "Sign up to take part in user research (opens in a new tab)"
survey_url: https://google.com
page_paths:
- /page-with-banners
- /recruitment-with-banners
38 changes: 38 additions & 0 deletions spec/requests/emergency_banner_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
RSpec.describe "Emergency Banners" do
context "getting a path with the banner partial" do
context "with the emergency banner active" do
before do
allow_any_instance_of(Redis).to receive(:hgetall).with("emergency_banner").and_return(
heading: "Emergency!",
campaign_class: "notable-death",
)
end

it "shows a banner in the page" do
get "/emergency"

expect(response.body).to include("Emergency!")
expect(response.body).not_to include("gem-c-emergency-banner__heading--homepage")
end

it "shows the homepage varient banner in the homepage" do
get "/"

expect(response.body).to include("Emergency!")
expect(response.body).to include("gem-c-emergency-banner__heading--homepage")
end
end

context "with the emergency banner inactive" do
before do
allow_any_instance_of(Redis).to receive(:hgetall).with("emergency_banner").and_return({})
end

it "does not show a banner in the page" do
get "/emergency"

expect(response.body).not_to include("Emergency!")
end
end
end
end
8 changes: 4 additions & 4 deletions spec/requests/recruitment_banner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@

context "with an active banner" do
it "shows a banner in the page" do
get "/page-with-banners"
get "/recruitment-with-banners"

expect(response.body).to include("This banner appears at /pages-with-banners!")
expect(response.body).to include("This banner appears at /recruitment-with-banners!")
end
end

context "with no active banner" do
it "does not show a banner in the page" do
get "/page-with-no-banners"
get "/recruitment-with-no-banners"

expect(response.body).not_to include("This banner appears at /pages-with-banners!")
expect(response.body).not_to include("This banner appears at /recruitment-with-banners!")
end
end
end
Expand Down
56 changes: 56 additions & 0 deletions spec/units/govuk_web_banners/emergency_banner_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
RSpec.describe GovukWebBanners::EmergencyBanner do
let(:redis_client) { instance_double(Redis, hgetall: {}) }

before do
Rails.application.config.emergency_banner_redis_client = redis_client
end

describe "caching" do
context "with a Rails cache" do
it "caches calls to the redis client for one minute" do
allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache.lookup_store(:memory_store))
Rails.cache.clear

described_class.new
described_class.new

expect(redis_client).to have_received(:hgetall).once

travel_to(Time.now + 61.seconds)

described_class.new
expect(redis_client).to have_received(:hgetall).twice

travel_back
end
end
end

describe "#active?" do
subject(:emergency_banner) { described_class.new }

context "with the emergency banner inactive" do
it "returns false" do
expect(emergency_banner.active?).to be false
end
end

context "with the emergency banner active" do
let(:redis_client) { instance_double(Redis, hgetall: { heading: "Emergency!", campaign_class: "notable-death" }) }

it "returns true" do
expect(emergency_banner.active?).to be true
end
end

context "with a failing call to Redis fails" do
before do
allow(redis_client).to receive(:hgetall).with("emergency_banner").and_raise(StandardError)
end

it "returns false" do
expect(emergency_banner.active?).to be false
end
end
end
end

0 comments on commit 82d8118

Please sign in to comment.