Skip to content

Commit

Permalink
Use GraphQL for rendering news articles
Browse files Browse the repository at this point in the history
This retrieves the content from Publishing API, instead of Content
Store, using a GraphQL query.

Note this has been done in the way that reduces the number of changes
needed in this application. As a result, we have introduced a small
"hack" to return the data in `GdsApi::Response` format but with this app
rewriting the hash keys to make them all underscore based.
  • Loading branch information
brucebolt committed Dec 16, 2024
1 parent ca8be12 commit 8bbf5f4
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ gem "rails", "7.2.2.1"
gem "bootsnap", require: false
gem "dalli"
gem "dartsass-rails"
gem "gds-api-adapters"
gem "gds-api-adapters", path: "../gds-api-adapters"
gem "govspeak"
gem "govuk_ab_testing"
gem "govuk_app_config"
Expand Down
21 changes: 13 additions & 8 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
PATH
remote: ../gds-api-adapters
specs:
gds-api-adapters (98.0.0)
activesupport
addressable
link_header
null_logger
plek (>= 1.9.0)
rack (>= 2.2.0)
rest-client (~> 2.0)

GEM
remote: https://rubygems.org/
specs:
Expand Down Expand Up @@ -123,13 +135,6 @@ GEM
execjs (2.9.1)
faker (3.4.2)
i18n (>= 1.8.11, < 2)
gds-api-adapters (97.6.0)
addressable
link_header
null_logger
plek (>= 1.9.0)
rack (>= 2.2.0)
rest-client (~> 2.0)
globalid (1.2.1)
activesupport (>= 6.1)
google-protobuf (4.29.1)
Expand Down Expand Up @@ -663,7 +668,7 @@ DEPENDENCIES
dalli
dartsass-rails
faker
gds-api-adapters
gds-api-adapters!
govspeak
govuk_ab_testing
govuk_app_config
Expand Down
34 changes: 28 additions & 6 deletions app/controllers/content_items_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,35 @@ def set_guide_draft_access_token
def load_content_item
content_item = Services.content_store.content_item(content_item_path)

content_item["links"]["ordered_related_items"] = ordered_related_items(content_item["links"]) if content_item["links"]
@content_item = if content_item["schema_name"] == "news_article" && serve_response_from_graphql?
graphql_response = Services
.publishing_api
.graphql_query(Graphql::NewsArticleQuery.new(content_item_path).query)

PresenterBuilder.new(
graphql_response,
content_item_path,
view_context,
).presenter
else
content_item["links"]["ordered_related_items"] = ordered_related_items(content_item["links"]) if content_item["links"]

PresenterBuilder.new(
content_item,
content_item_path,
view_context,
).presenter
end
end

@content_item = PresenterBuilder.new(
content_item,
content_item_path,
view_context,
).presenter
def serve_response_from_graphql?
if params.include?(:graphql) && params[:graphql] == "true"
true
elsif params.include?(:graphql) && params[:graphql] == "false"
false
else
Features.graphql_feature_enabled?
end
end

def ordered_related_items(links)
Expand Down
75 changes: 75 additions & 0 deletions app/queries/graphql/news_article_query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
class Graphql::NewsArticleQuery
def initialize(base_path)
@base_path = base_path
end

def query
<<-QUERY
{
edition(
basePath: "#{@base_path}",
contentStore: "live",
) {
... on NewsArticle {
basePath
description
details
documentType
firstPublishedAt
links {
availableTranslations {
basePath
locale
}
government {
details {
current
}
title
}
organisations {
basePath
contentId
title
}
people {
basePath
contentId
title
}
taxons {
basePath
contentId
documentType
phase
title
links {
parentTaxons {
basePath
contentId
documentType
phase
title
}
}
}
topicalEvents {
basePath
contentId
title
}
worldLocations {
basePath
contentId
title
}
}
locale
schemaName
title
}
}
}
QUERY
end
end
38 changes: 38 additions & 0 deletions test/controllers/content_items_controller_test.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
require "test_helper"
require "gds_api/test_helpers/publishing_api"

class ContentItemsControllerTest < ActionController::TestCase
include GdsApi::TestHelpers::ContentStore
include GdsApi::TestHelpers::PublishingApi
include GovukAbTesting::MinitestHelpers

test "routing handles paths with no format or locale" do
Expand Down Expand Up @@ -124,6 +126,42 @@ class ContentItemsControllerTest < ActionController::TestCase
assert_response :not_acceptable
end

test "with the GraphQL feature flag enabled gets item from GraphQL if it is a news article" do
Features.stubs(:graphql_feature_enabled?).returns(true)

content_item = content_store_has_schema_example("news_article", "news_article")
base_path = content_item["base_path"]

graphql_fixture = fetch_graphql_fixture("news_article")
stub_publishing_api_graphql_query(Graphql::NewsArticleQuery.new(base_path).query, graphql_fixture)

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

assert_requested :post, "#{PUBLISHING_API_ENDPOINT}/graphql",
body: { query: Graphql::NewsArticleQuery.new(base_path).query },
times: 1

assert_response :success
end

test "with the GraphQL feature flag enabled does not get item from GraphQL if it is not a news article" do
Features.stubs(:graphql_feature_enabled?).returns(true)

content_item = content_store_has_schema_example("case_study", "case_study")

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

assert_not_requested :post, "#{PUBLISHING_API_ENDPOINT}/graphql"

assert_response :success
end

test "gets item from content store" do
content_item = content_store_has_schema_example("case_study", "case_study")

Expand Down
72 changes: 72 additions & 0 deletions test/fixtures/graphql/news_article.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"data": {
"edition": {
"basePath": "/government/news/announcement",
"description": "Summary of the news",
"details": {
"body": "Some text",
"political": "true"
},
"documentType": "news_story",
"firstPublishedAt": "2016-12-25T01:02:03+00:00",
"links": {
"availableTranslations": [
{
"basePath": "/government/news/announcement",
"locale": "en"
}
],
"government": [
{
"details": {
"current": false
},
"title": "A government"
}
],
"organisations": [
{
"basePath": "/organisation-1",
"title": "An organisation"
}
],
"people": [
{
"basePath": "/person-1",
"title": "A person"
}
],
"taxons": [
{
"basePath": "/taxon-2",
"contentId": "8fa1fa2f-bcce-4cde-98fb-308514c35c63",
"title": "Taxon 2",
"links": {
"parentTaxons": [
{
"basePath": "/taxon-1",
"contentId": "9de167ce-6c4f-40b1-a45e-e3160d75556e",
"title": "Taxon 1"
}
]
}
}
],
"topicalEvents": [
{
"basePath": "/topical-event-1",
"title": "A topical event"
}
],
"worldLocations": [
{
"basePath": "/world-location-1",
"title": "A world location"
}
]
},
"schemaName": "news_article",
"title": "Generic news article"
}
}
}
14 changes: 14 additions & 0 deletions test/presenter_test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,17 @@ def schema_item(type = schema_name, schema = schema_name)
govuk_content_schema_example(schema, type)
end
end

class GraphqlPresenterTestCase < PresenterTestCase
def create_presenter(presenter_class,
content_item: fetch_graphql_fixture("news_article"),
requested_path: "/test-content-item",
view_context: ApplicationController.new.view_context)
presenter_class.new(content_item, requested_path, view_context)
end

def presented_item(type = schema_name, overrides = {})
example = fetch_graphql_fixture(type).dig("data", "edition").deep_transform_keys(&:underscore)
present_example(example.merge(overrides))
end
end
33 changes: 33 additions & 0 deletions test/presenters/news_article_graphql_presenter_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require "presenter_test_helper"

class NewsArticleGraphqlPresenterTest
class NewsArticleGraphqlPresenterTestCase < GraphqlPresenterTestCase
attr_accessor :example_schema_name

def schema_name
"news_article"
end
end

class PresentedNewsArticleGraphqlTest < NewsArticleGraphqlPresenterTestCase
test "presents a description" do
assert_equal "Summary of the news", presented_item.description
end

test "presents a body" do
assert_equal "Some text", presented_item.body
end

test "presents a readable first published date" do
assert_equal "25 December 2016", presented_item.published
end

test "presents the locale" do
assert_equal "en", presented_item.locale
end

test "presents historically political" do
assert presented_item.historically_political?
end
end
end
7 changes: 7 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,10 @@ def single_page_notification_button_ga4_tracking(index_link, section)
}
end
end

def fetch_graphql_fixture(filename)
json = File.read(
Rails.root.join("test", "fixtures", "graphql", "#{filename}.json"),
)
JSON.parse(json)
end

0 comments on commit 8bbf5f4

Please sign in to comment.