From 6888f79f86734f2d4eb750a457d3d1ff0a21c30b 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. 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. --- app/controllers/content_items_controller.rb | 34 +++++++-- app/queries/graphql/news_article_query.rb | 75 +++++++++++++++++++ .../content_items_controller_test.rb | 38 ++++++++++ test/fixtures/graphql/news_article.json | 72 ++++++++++++++++++ test/presenter_test_helper.rb | 14 ++++ .../news_article_graphql_presenter_test.rb | 33 ++++++++ test/test_helper.rb | 7 ++ 7 files changed, 267 insertions(+), 6 deletions(-) create mode 100644 app/queries/graphql/news_article_query.rb create mode 100644 test/fixtures/graphql/news_article.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 fce914e36..d7a328a8b 100644 --- a/app/controllers/content_items_controller.rb +++ b/app/controllers/content_items_controller.rb @@ -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) diff --git a/app/queries/graphql/news_article_query.rb b/app/queries/graphql/news_article_query.rb new file mode 100644 index 000000000..ba1beb51e --- /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( + 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 diff --git a/test/controllers/content_items_controller_test.rb b/test/controllers/content_items_controller_test.rb index 5ad576330..5d69727fe 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 @@ -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") diff --git a/test/fixtures/graphql/news_article.json b/test/fixtures/graphql/news_article.json new file mode 100644 index 000000000..00ea33bef --- /dev/null +++ b/test/fixtures/graphql/news_article.json @@ -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" + } + } +} diff --git a/test/presenter_test_helper.rb b/test/presenter_test_helper.rb index a9d244738..e9931f26b 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_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 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..366f495ac 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -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