Skip to content

Commit

Permalink
Display new recommended related links
Browse files Browse the repository at this point in the history
This commit adds support for the new recommended related links to be shown based on the existence of a HTTP header set by our CDN. To achieve this we add support in general for feature flags via a `FeatureToggler` class.

Should the header exist, the new recommended related links will only be shown if there are no existing `ordered_related_items` in the content item, to ensure that we do not override manually curated links.

Solo: @karlbaker02
  • Loading branch information
Karl Baker committed Jun 25, 2019
1 parent 9063df3 commit 4e92118
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 0 deletions.
5 changes: 5 additions & 0 deletions app/controllers/content_items_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ def set_guide_draft_access_token

def load_content_item
content_item = Services.content_store.content_item(content_item_path)

if Services.feature_toggler.use_recommended_related_links?(content_item['links'], request.headers)
content_item['links']['ordered_related_items'] = content_item['links'].fetch('suggested_ordered_related_items', [])
end

@content_item = PresenterBuilder.new(content_item, content_item_path).presenter
end

Expand Down
6 changes: 6 additions & 0 deletions app/lib/services.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ def self.content_store
disable_cache: true,
)
end

def self.feature_toggler
@feature_toggler ||= FeatureToggler.new(
HttpFeatureFlags.instance
)
end
end
12 changes: 12 additions & 0 deletions app/models/feature_toggler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class FeatureToggler
def initialize(feature_flags)
@feature_flags = feature_flags
end

def use_recommended_related_links?(content_item_links, request_headers)
return false if content_item_links.nil?

content_item_links.fetch('ordered_related_items', []).empty? &&
@feature_flags.feature_enabled?(FeatureFlagNames.recommended_related_links, request_headers)
end
end
17 changes: 17 additions & 0 deletions app/models/http_feature_flags.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class HttpFeatureFlags
def initialize
@feature_flags = {}
end

def add_http_feature_flag(header_name, val)
@feature_flags[header_name] = val
end

def feature_enabled?(header_name, request_headers)
@feature_flags.has_key?(header_name) && @feature_flags[header_name] == request_headers[header_name]
end

def self.instance
@instance ||= HttpFeatureFlags.new
end
end
7 changes: 7 additions & 0 deletions config/initializers/feature_flags.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module FeatureFlagNames
def self.recommended_related_links
'HTTP_GOVUK_USE_RECOMMENDED_RELATED_LINKS'
end
end

HttpFeatureFlags.instance.add_http_feature_flag(FeatureFlagNames.recommended_related_links, 'true')
32 changes: 32 additions & 0 deletions test/controllers/content_items_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,38 @@ class ContentItemsControllerTest < ActionController::TestCase
assert_equal content_item['title'], assigns[:content_item].title
end

test "gets item from content store and keeps existing ordered_related_items when feature flag header not specified" do
content_item = content_store_has_schema_example('case_study', 'case_study')

get :show, params: { path: path_for(content_item) }
assert_response :success
assert_equal content_item['links']['ordered_related_items'], assigns[:content_item].content_item['links']['ordered_related_items']
end

test "gets item from content store and keep existing ordered_related_items when feature flag header is specified but links already exist" do
request.headers[FeatureFlagNames.recommended_related_links] = 'true'

content_item = content_store_has_schema_example('guide', 'guide')

get :show, params: { path: path_for(content_item) }
assert_response :success
refute_empty content_item['links']['ordered_related_items'], 'Content item should have existing related links'
refute_empty content_item['links']['suggested_ordered_related_items'], 'Content item should have existing suggested related links'
assert_equal content_item['links']['ordered_related_items'], assigns[:content_item].content_item['links']['ordered_related_items']
end

test "gets item from content store and replaces ordered_related_items when feature flag header is specified and there are no existing links" do
request.headers[FeatureFlagNames.recommended_related_links] = 'true'

content_item = content_store_has_schema_example('case_study', 'case_study')

get :show, params: { path: path_for(content_item) }
assert_response :success
assert_empty content_item['links']['ordered_related_items'], 'Content item should not have existing related links'
refute_empty content_item['links']['suggested_ordered_related_items'], 'Content item should have existing suggested related links'
assert_equal assigns[:content_item].content_item['links']['ordered_related_items'], content_item['links']['suggested_ordered_related_items']
end

test "sets the expiry as sent by content-store" do
content_item = content_store_has_schema_example('coming_soon', 'coming_soon')
content_store_has_item(content_item['base_path'], content_item, max_age: 20)
Expand Down
59 changes: 59 additions & 0 deletions test/models/feature_toggler_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
require 'test_helper'

class FeatureTogglerTest < ActiveSupport::TestCase
test 'use_recommended_related_links returns false when content item has no links attribute' do
content_item = {}
instance = setup_feature_toggler_with_feature_enabled(true)

use_recommended_links = instance.use_recommended_related_links?(content_item['links'], @request_headers)

assert_equal(false, use_recommended_links)
end

test 'use_recommended_related_links returns false when content item has existing ordered_related_items' do
content_item = { "links" => { "ordered_related_items" => [{ "content_id" => "1234" }] } }
instance = setup_feature_toggler_with_feature_enabled(true)

use_recommended_links = instance.use_recommended_related_links?(content_item['links'], @request_headers)

assert_equal(false, use_recommended_links)
end

test 'use_recommended_related_links returns false when headers are not correct' do
content_item = { "links" => {} }
instance = setup_feature_toggler_with_feature_enabled(false)

use_recommended_links = instance.use_recommended_related_links?(content_item['links'], {})

assert_equal(false, use_recommended_links)
end

test 'use_recommended_related_links returns true when content item has no ordered_related_items attribute and headers are correct' do
content_item = { "links" => {} }
instance = setup_feature_toggler_with_feature_enabled(true)

use_recommended_links = instance.use_recommended_related_links?(content_item['links'], @request_headers)

assert_equal(true, use_recommended_links)
end

test 'use_recommended_related_links returns true when content item has no items in ordered_related_items attribute and headers are correct' do
content_item = { "links" => { "ordered_related_items" => [] } }
instance = setup_feature_toggler_with_feature_enabled(true)

use_recommended_links = instance.use_recommended_related_links?(content_item['links'], @request_headers)

assert_equal(true, use_recommended_links)
end

def setup
@request_headers = { 'HTTP_GOVUK_USE_RECOMMENDED_RELATED_LINKS': 'true' }
end

def setup_feature_toggler_with_feature_enabled(feature_enabled)
feature_flags = HttpFeatureFlags.new
feature_flags.stubs(:feature_enabled?).returns(feature_enabled)

FeatureToggler.new(feature_flags)
end
end
55 changes: 55 additions & 0 deletions test/models/http_feature_flags_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
require 'test_helper'

class HttpFeatureFlagsTest < ActiveSupport::TestCase
test 'instance should create a singleton instance' do
instance = HttpFeatureFlags.instance
instance.add_http_feature_flag('TEST_HEADER', 'show')

new_instance = HttpFeatureFlags.instance
feature_enabled = new_instance.feature_enabled?('TEST_HEADER', 'TEST_HEADER' => 'show')

assert_equal(true, feature_enabled)
end

test 'add_http_feature_flag should set a new feature flag' do
instance = HttpFeatureFlags.new

feature_enabled = instance.feature_enabled?('USE_MAGIC', 'USE_MAGIC' => 'only_at_weekends')
assert_equal(false, feature_enabled)

instance.add_http_feature_flag('USE_MAGIC', 'only_at_weekends')
feature_enabled = instance.feature_enabled?('USE_MAGIC', 'USE_MAGIC' => 'only_at_weekends')
assert_equal(true, feature_enabled)
end

test 'feature_enabled? should return false when feature flag has not been set' do
instance = HttpFeatureFlags.new

feature_enabled = instance.feature_enabled?('USE_MAGIC', 'USE_MAGIC' => 'only_at_weekends')
assert_equal(false, feature_enabled)
end

test 'feature_enabled? should return false when header has not been set' do
instance = HttpFeatureFlags.new

instance.add_http_feature_flag('USE_MAGIC', 'only_at_weekends')
feature_enabled = instance.feature_enabled?('USE_MAGIC', {})
assert_equal(false, feature_enabled)
end

test 'feature_enabled? should return false when header has been set but does not match specified value' do
instance = HttpFeatureFlags.new

instance.add_http_feature_flag('USE_MAGIC', 'only_at_weekends')
feature_enabled = instance.feature_enabled?('USE_MAGIC', 'USE_MAGIC' => 'all_the_time')
assert_equal(false, feature_enabled)
end

test 'feature_enabled? should return true when headers has been set and matches specified value' do
instance = HttpFeatureFlags.new

instance.add_http_feature_flag('USE_MAGIC', 'only_at_weekends')
feature_enabled = instance.feature_enabled?('USE_MAGIC', 'USE_MAGIC' => 'only_at_weekends')
assert_equal(true, feature_enabled)
end
end

0 comments on commit 4e92118

Please sign in to comment.