From 90f8d4dca650835f2f1ccd588f964855c0eabf43 Mon Sep 17 00:00:00 2001 From: Bruce Bolt Date: Tue, 3 Dec 2024 11:53:07 +0000 Subject: [PATCH] Use GraphQL for rendering news articles 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. --- app/controllers/content_items_controller.rb | 32 +++++++- app/queries/graphql/news_article_query.rb | 75 +++++++++++++++++++ .../content_items_controller_test.rb | 52 +++++++++++++ .../graphql/case_study_schema_name.json | 9 +++ test/fixtures/graphql/news_article.json | 74 ++++++++++++++++++ .../graphql/news_article_schema_name.json | 9 +++ test/presenter_test_helper.rb | 14 ++++ .../news_article_graphql_presenter_test.rb | 33 ++++++++ test/test_helper.rb | 11 +++ 9 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 app/queries/graphql/news_article_query.rb create mode 100644 test/fixtures/graphql/case_study_schema_name.json create mode 100644 test/fixtures/graphql/news_article.json create mode 100644 test/fixtures/graphql/news_article_schema_name.json create mode 100644 test/presenters/news_article_graphql_presenter_test.rb diff --git a/app/controllers/content_items_controller.rb b/app/controllers/content_items_controller.rb index 0bf57fef6..f5a914fbf 100644 --- a/app/controllers/content_items_controller.rb +++ b/app/controllers/content_items_controller.rb @@ -136,17 +136,47 @@ def set_guide_draft_access_token end def load_content_item + @content_item = if use_graphql? + graphql_response = Services + .publishing_api + .graphql_content_item(Graphql::NewsArticleQuery.new(content_item_path).query) + + if graphql_response["schema_name"] == "news_article" + PresenterBuilder.new( + graphql_response, + content_item_path, + view_context, + ).presenter + else + load_content_item_from_content_store + end + else + load_content_item_from_content_store + end + end + + def load_content_item_from_content_store 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 = PresenterBuilder.new( + PresenterBuilder.new( content_item, content_item_path, view_context, ).presenter end + def use_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) return [] if links["ordered_related_items_overrides"].present? diff --git a/app/queries/graphql/news_article_query.rb b/app/queries/graphql/news_article_query.rb new file mode 100644 index 000000000..07d11aa58 --- /dev/null +++ b/app/queries/graphql/news_article_query.rb @@ -0,0 +1,75 @@ +class Graphql::NewsArticleQuery + def initialize(base_path) + @base_path = base_path + end + + def query + <<-QUERY + { + edition( + base_path: "#{@base_path}", + content_store: "live", + ) { + ... on NewsArticle { + base_path + description + details + document_type + first_published_at + links { + available_translations { + base_path + locale + } + government { + details { + current + } + title + } + organisations { + base_path + content_id + title + } + people { + base_path + content_id + title + } + taxons { + base_path + content_id + document_type + phase + title + links { + parent_taxons { + base_path + content_id + document_type + phase + title + } + } + } + topical_events { + base_path + content_id + title + } + world_locations { + base_path + content_id + title + } + } + locale + schema_name + title + } + } + } + QUERY + end +end diff --git a/test/controllers/content_items_controller_test.rb b/test/controllers/content_items_controller_test.rb index f0aa91dfa..c979dd226 100644 --- a/test/controllers/content_items_controller_test.rb +++ b/test/controllers/content_items_controller_test.rb @@ -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 @@ -103,6 +105,56 @@ 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) + base_path = "content-item" + + graphql_schema_name_fixture = fetch_graphql_fixture("news_article_schema_name") + stub_publishing_api_graphql_content_item(Graphql::SchemaNameQuery.new("/#{base_path}").query, graphql_schema_name_fixture) + + graphql_fixture = fetch_graphql_fixture("news_article") + stub_publishing_api_graphql_content_item(Graphql::NewsArticleQuery.new("/#{base_path}").query, graphql_fixture) + + get :show, + params: { + path: base_path, + } + + assert_requested :post, "#{PUBLISHING_API_ENDPOINT}/graphql", + body: { query: Graphql::SchemaNameQuery.new("/#{base_path}").query }, + times: 1 + + 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") + base_path = path_for(content_item) + + graphql_schema_name_fixture = fetch_graphql_fixture("case_study_schema_name") + stub_publishing_api_graphql_content_item(Graphql::SchemaNameQuery.new("/#{base_path}").query, graphql_schema_name_fixture) + + get :show, + params: { + path: path_for(content_item), + } + + assert_requested :post, "#{PUBLISHING_API_ENDPOINT}/graphql", + body: { query: Graphql::SchemaNameQuery.new("/#{base_path}").query }, + times: 1 + + assert_not_requested :post, "#{PUBLISHING_API_ENDPOINT}/graphql", + body: { query: Graphql::NewsArticleQuery.new("/#{base_path}").query } + + assert_response :success + end + test "gets item from content store" do content_item = content_store_has_schema_example("case_study", "case_study") diff --git a/test/fixtures/graphql/case_study_schema_name.json b/test/fixtures/graphql/case_study_schema_name.json new file mode 100644 index 000000000..ccfc31829 --- /dev/null +++ b/test/fixtures/graphql/case_study_schema_name.json @@ -0,0 +1,9 @@ +{ + "data": + { + "edition": + { + "schema_name": "case_study" + } + } +} diff --git a/test/fixtures/graphql/news_article.json b/test/fixtures/graphql/news_article.json new file mode 100644 index 000000000..3623f6977 --- /dev/null +++ b/test/fixtures/graphql/news_article.json @@ -0,0 +1,74 @@ +{ + "data": + { + "edition": + { + "base_path": "/government/news/announcement", + "description": "Summary of the news", + "details": { + "body": "Some text", + "political": "true" + }, + "document_type": "news_story", + "first_published_at": "2016-12-25T01:02:03+00:00", + "links": { + "available_translations": [ + { + "base_path": "/government/news/announcement", + "locale": "en" + } + ], + "government": [ + { + "details": { + "current": false + }, + "title": "A government" + } + ], + "organisations": [ + { + "base_path": "/organisation-1", + "title": "An organisation" + } + ], + "people": [ + { + "base_path": "/person-1", + "title": "A person" + } + ], + "taxons": [ + { + "base_path": "/taxon-2", + "content_id": "8fa1fa2f-bcce-4cde-98fb-308514c35c63", + "title": "Taxon 2", + "links": { + "parent_taxons": [ + { + "base_path": "/taxon-1", + "content_id": "9de167ce-6c4f-40b1-a45e-e3160d75556e", + "title": "Taxon 1" + } + ] + } + } + ], + "topical_events": [ + { + "base_path": "/topical-event-1", + "title": "A topical event" + } + ], + "world_locations": [ + { + "base_path": "/world-location-1", + "title": "A world location" + } + ] + }, + "schema_name": "news_article", + "title": "Generic news article" + } + } +} diff --git a/test/fixtures/graphql/news_article_schema_name.json b/test/fixtures/graphql/news_article_schema_name.json new file mode 100644 index 000000000..11619260a --- /dev/null +++ b/test/fixtures/graphql/news_article_schema_name.json @@ -0,0 +1,9 @@ +{ + "data": + { + "edition": + { + "schema_name": "news_article" + } + } +} diff --git a/test/presenter_test_helper.rb b/test/presenter_test_helper.rb index a9d244738..c17b6d478 100644 --- a/test/presenter_test_helper.rb +++ b/test/presenter_test_helper.rb @@ -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_content_item("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_content_item(type) + present_example(example.merge(overrides)) + end +end diff --git a/test/presenters/news_article_graphql_presenter_test.rb b/test/presenters/news_article_graphql_presenter_test.rb new file mode 100644 index 000000000..51dc26cca --- /dev/null +++ b/test/presenters/news_article_graphql_presenter_test.rb @@ -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 diff --git a/test/test_helper.rb b/test/test_helper.rb index 4d24c0ee4..6418e679b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -280,3 +280,14 @@ 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 + +def fetch_graphql_content_item(filename) + fetch_graphql_fixture(filename).dig("data", "edition") +end