Skip to content

Commit

Permalink
Merge pull request #257 from alphagov/travel-advice-atom
Browse files Browse the repository at this point in the history
Add atom feeds to travel advice
  • Loading branch information
fofr authored Mar 3, 2017
2 parents 45c5535 + 89990f8 commit 039651b
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 21 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ gem 'rails-i18n', '>= 4.0.4'
gem 'rails_translation_manager', '~> 0.0.2'
gem 'rails-controller-testing', '~> 0.1'
gem 'govuk_ab_testing', '1.0.1'
gem 'htmlentities', '4.3.4'

if ENV['API_DEV']
gem 'gds-api-adapters', path: '../gds-api-adapters'
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ GEM
sass (>= 3.2.0)
govuk_navigation_helpers (3.0.0)
gds-api-adapters (~> 40.1)
htmlentities (4.3.4)
http-cookie (1.0.3)
domain_name (~> 0.5)
i18n (0.7.0)
Expand Down Expand Up @@ -260,6 +261,7 @@ DEPENDENCIES
govuk_ab_testing (= 1.0.1)
govuk_frontend_toolkit (= 5.1.0)
govuk_navigation_helpers (~> 3.0)
htmlentities (= 4.3.4)
jasmine-rails (~> 0.14.0)
logstasher (= 0.6.1)
mocha
Expand Down
12 changes: 11 additions & 1 deletion app/controllers/content_items_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
class ContentItemsController < ApplicationController
rescue_from GdsApi::HTTPForbidden, with: :error_403
rescue_from GdsApi::HTTPNotFound, with: :error_notfound
rescue_from ActionView::MissingTemplate, with: :error_406

attr_accessor :content_item

Expand Down Expand Up @@ -69,7 +70,12 @@ def with_locale
end

def content_item_path
'/' + URI.encode(params[:path])
path_and_optional_locale = params
.values_at(:path, :locale)
.compact
.join('.')

'/' + URI.encode(path_and_optional_locale)
end

def content_store
Expand All @@ -83,4 +89,8 @@ def error_403(exception)
def error_notfound
render plain: 'Not found', status: :not_found
end

def error_406
render plain: 'Not acceptable', status: 406
end
end
25 changes: 20 additions & 5 deletions app/presenters/travel_advice_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ def title_and_context
}
end

def country_name
content_item["details"]["country"]["name"]
end

def related_items
items = ordered_related_items.map do |link|
{
Expand Down Expand Up @@ -112,6 +116,18 @@ def alert_status
alert_statuses.join('').html_safe
end

def atom_change_description
simple_format(HTMLEntities.new.encode(change_description, :basic, :decimal))
end

def atom_public_updated_at
DateTime.parse(content_item["public_updated_at"])
end

def web_url
Plek.current.website_root + content_item["base_path"]
end

private

def summary_part
Expand All @@ -129,10 +145,6 @@ def current_part
end
end

def country_name
content_item["details"]["country"]["name"]
end

def parts
content_item["details"]["parts"] || []
end
Expand Down Expand Up @@ -163,6 +175,10 @@ def ordered_related_items
content_item["links"]["ordered_related_items"] || []
end

def change_description
content_item['details']['change_description']
end

# FIXME: Update publishing app UI and remove from content
# Change description is used as "Latest update" but isn't labelled that way
# in the publisher. The frontend didn't add this label before.
Expand All @@ -171,7 +187,6 @@ def ordered_related_items
# has a latest update label, so we can strip this out.
# Avoids: "Latest update: Latest update - …"
def latest_update
change_description = content_item['details']['change_description']
change_description.sub(/^Latest update:?\s-?\s?/i, '').tap do |latest|
latest[0] = latest[0].capitalize
end
Expand Down
14 changes: 14 additions & 0 deletions app/views/content_items/travel_advice.atom.builder
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
atom_feed(root_url: @content_item.web_url, id: @content_item.web_url) do |feed|
feed.title("Travel Advice Summary")
feed.updated @content_item.atom_public_updated_at
feed.author do |author|
author.name "GOV.UK"
end
feed.entry(@content_item, id: "#{@content_item.web_url}##{@content_item.atom_public_updated_at}", url: @content_item.web_url, updated: @content_item.atom_public_updated_at) do |entry|
entry.title(@content_item.title)
entry.link(:rel => "self", :type => "application/atom+xml", :href => "#{@content_item.web_url}.atom")
entry.summary(:type => :xhtml) do |summary|
summary << @content_item.atom_change_description
end
end
end
5 changes: 4 additions & 1 deletion app/views/content_items/travel_advice.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<%
content_for :title, @content_item.page_title
content_for :simple_header, true
%>
content_for :extra_head_content do %>
<%= auto_discovery_link_tag :atom, @content_item.feed_link,
title: "Recent updates for #{@content_item.country_name}" %>
<% end %>
<div class="grid-row">
<div class="column-two-thirds">
<%= render 'govuk_component/title', @content_item.title_and_context %>
Expand Down
16 changes: 10 additions & 6 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
mount JasmineRails::Engine => "/specs" if defined?(JasmineRails)

with_options format: false do |r|
r.get 'healthcheck', to: proc { [200, {}, ['']] }
# FIXME: Update when https://trello.com/c/w8HN3M4A/ is ready
r.get 'foreign-travel-advice/:country/:part' => 'travel_advice#show'
r.get '*path' => 'content_items#show', constraints: { path: %r[.*] }
end
get 'healthcheck', to: proc { [200, {}, ['']] }

# FIXME: Update when https://trello.com/c/w8HN3M4A/ is ready
get 'foreign-travel-advice/:country/:part' => 'travel_advice#show'

get '*path(.:locale)(.:format)' => 'content_items#show',
constraints: {
format: /atom/,
locale: /\w{2}(-[\d\w]{2,3})?/,
}
end
69 changes: 61 additions & 8 deletions test/controllers/content_items_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,46 @@ class ContentItemsControllerTest < ActionController::TestCase
ENV['ENABLE_NEW_NAVIGATION'] = nil
end

test "routing handles translated content paths" do
translated_path = 'government/case-studies/allez.fr'
test 'routing handles paths with no format or locale' do
assert_routing(
'/government/news/statement-the-status-of-eu-nationals-in-the-uk',
controller: 'content_items',
action: 'show',
path: 'government/news/statement-the-status-of-eu-nationals-in-the-uk',
)
end

test 'routing handles paths for all supported locales' do
I18n.available_locales.each do |locale|
assert_routing(
"/government/news/statement-the-status-of-eu-nationals-in-the-uk.#{locale}",
controller: 'content_items',
action: 'show',
path: 'government/news/statement-the-status-of-eu-nationals-in-the-uk',
locale: locale.to_s
)
end
end

test 'routing handles paths with just format' do
assert_routing(
'/government/news/statement-the-status-of-eu-nationals-in-the-uk.atom',
controller: 'content_items',
action: 'show',
path: 'government/news/statement-the-status-of-eu-nationals-in-the-uk',
format: 'atom',
)
end

assert_routing({ path: translated_path, method: :get },
controller: 'content_items', action: 'show', path: translated_path)
test 'routing handles paths with format and locale' do
assert_routing(
'/government/news/statement-the-status-of-eu-nationals-in-the-uk.es.atom',
controller: 'content_items',
action: 'show',
path: 'government/news/statement-the-status-of-eu-nationals-in-the-uk',
format: 'atom',
locale: 'es'
)
end

test "gets item from content store" do
Expand Down Expand Up @@ -47,14 +82,23 @@ class ContentItemsControllerTest < ActionController::TestCase

test "renders translated content items in their locale" do
content_item = content_store_has_schema_example('case_study', 'translated')
translated_format_name = I18n.t("content_item.format.case_study", count: 1, locale: 'es')
locale = content_item['locale']
translated_format_name = I18n.t("content_item.format.case_study", count: 1, locale: locale)

get :show, params: { path: path_for(content_item) }
get :show, params: { path: path_for(content_item, locale), locale: locale }

assert_response :success
assert_select "title", %r(#{translated_format_name})
end

test "renders atom feeds" do
content_item = content_store_has_schema_example('travel_advice', 'full-country')
get :show, params: { path: path_for(content_item), format: 'atom' }

assert_response :success
assert_select "feed title", 'Travel Advice Summary'
end

test "gets item from content store even when url contains multi-byte UTF8 character" do
content_item = content_store_has_schema_example('case_study', 'case_study')
utf8_path = "government/case-studies/caf\u00e9-culture"
Expand Down Expand Up @@ -84,6 +128,13 @@ class ContentItemsControllerTest < ActionController::TestCase
assert_response :forbidden
end

test "returns 406 for schema types which don't support provided format" do
content_item_without_atom = content_store_has_schema_example('case_study', 'case_study')
get :show, params: { path: path_for(content_item_without_atom), format: 'atom' }

assert_response :not_acceptable
end

test "defaults to 'A' view without AB Testing cookie for Detailed Guides" do
content_item = content_store_has_schema_example('detailed_guide', 'detailed_guide')
path = 'government/abtest/detailed-guide'
Expand Down Expand Up @@ -172,8 +223,10 @@ class ContentItemsControllerTest < ActionController::TestCase
assert_response_not_modified_for_ab_test
end

def path_for(content_item)
content_item['base_path'].sub(/^\//, '')
def path_for(content_item, locale = nil)
base_path = content_item['base_path'].sub(/^\//, '')
base_path.gsub!(/\.#{locale}$/, '') if locale
base_path
end

def taxonomy_sidebar
Expand Down
41 changes: 41 additions & 0 deletions test/integration/travel_advice_atom_feed_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'test_helper'

class TravelAdviceAtomFeed < ActionDispatch::IntegrationTest
setup do
setup_and_visit_travel_advice_atom_feed('full-country')
@base_path = @content_item['base_path']
@updated_at = DateTime.parse(@content_item["public_updated_at"]).rfc3339
end

test "it sets the alternative link correctly" do
assert page.has_css?("//feed/link[rel='alternate'][href$='#{@base_path}']")
end

test "it sets the entry's id to the url concatenated with updated_at" do
id = page.find("//feed/entry/id").text
assert id.end_with?("#{@base_path}##{@updated_at}")
end

test "it sets the entry's title correctly" do
title = page.find("//feed/entry/title").text
assert_equal title, @content_item['title']
end

test "it sets the entry's summary correctly" do
summary = page.find("//feed/entry/summary").text
assert_equal summary, @content_item['details']['change_description'].strip
end

test "it sets the entry's updated correctly" do
updated = page.find("//feed/entry/updated").text
assert_equal updated, @updated_at
end

def setup_and_visit_travel_advice_atom_feed(name)
example = get_content_example_by_format_and_name('travel_advice', name)
@content_item = JSON.parse(example).tap do |item|
content_store_has_item(item["base_path"], item.to_json)
visit "#{item['base_path']}.atom"
end
end
end
5 changes: 5 additions & 0 deletions test/integration/travel_advice_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ class TravelAdviceTest < ActionDispatch::IntegrationTest
refute page.has_css?('.part-navigation li a', text: first_part['title'])
end

test "travel advice includes a discoverable atom feed link" do
setup_and_visit_content_item('full-country')
assert page.has_css?("link[type*='atom'][href='#{@content_item['base_path']}.atom']", visible: false)
end

def setup_and_visit_travel_advice_part(name, part)
@content_item = JSON.parse(get_content_example(name)).tap do |item|
content_store_has_item(item["base_path"], item.to_json)
Expand Down
6 changes: 6 additions & 0 deletions test/presenters/travel_advice_presenter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ def format_name
assert_equal nil, presented.map_download_url
end

test "formats change description for an atom feed" do
example = schema_item("full-country")
example['details']['change_description'] = 'Test<br>XML'
assert_equal "<p>Test&lt;br&gt;XML</p>", present_example(example).atom_change_description
end

private

def present_latest(latest)
Expand Down
17 changes: 17 additions & 0 deletions test/wraith/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ paths:
topical_event_about_page_8: '/government/topical-events/farming/about'
topical_event_about_page_9: '/government/topical-events/girl-summit-2014/about'

# Most visited travel advice (2nd March 2017)
travel_advice_1: '/foreign-travel-advice/usa/entry-requirements'
travel_advice_2: '/foreign-travel-advice/turkey'
travel_advice_3: '/foreign-travel-advice/usa'
travel_advice_4: '/foreign-travel-advice/egypt'
travel_advice_5: '/foreign-travel-advice/morocco'
travel_advice_6: '/foreign-travel-advice/spain'
travel_advice_7: '/foreign-travel-advice/india/entry-requirements'
travel_advice_8: '/foreign-travel-advice/australia/entry-requirements'
travel_advice_9: '/foreign-travel-advice/thailand'
travel_advice_10: '/foreign-travel-advice/france'

# Travel advice atom feeds
travel_advice_atom_1: '/foreign-travel-advice/turkey.atom'
travel_advice_atom_2: '/foreign-travel-advice/usa.atom'
travel_advice_atom_3: '/foreign-travel-advice/egypt.atom'

working_group_long: '/government/groups/benefits-credits-consultation-group-bccg'
working_group_short: '/government/groups/aviation-statistics-team'
working_group_with_policies: '/government/groups/better-regulation-executive'
Expand Down

0 comments on commit 039651b

Please sign in to comment.