diff --git a/app/graphql/types/edition_type.rb b/app/graphql/types/edition_type.rb index a93e740f36..2983a17c37 100644 --- a/app/graphql/types/edition_type.rb +++ b/app/graphql/types/edition_type.rb @@ -153,17 +153,6 @@ class WhipOrganisation < Types::BaseObject field :supports_historical_accounts, Boolean field :whip_organisation, WhipOrganisation field :world_locations, [EditionType], null: false - - def body - return object[:body] if object[:body].is_a?(String) - - govspeak = object.fetch(:body, []) - .filter { _1[:content_type] == "text/govspeak" } - .map { _1[:content] } - .first - - Govspeak::Document.new(govspeak).to_html if govspeak.present? - end end field :active, Boolean, null: false @@ -197,6 +186,16 @@ def body field :web_url, String field :withdrawn_notice, WithdrawnNotice + def details + Presenters::ContentTypeResolver.new("text/html").resolve( + Presenters::DetailsPresenter.new( + object.details, + Presenters::ChangeHistoryPresenter.new(object), + Presenters::ContentEmbedPresenter.new(object), + ).details, + ) + end + def withdrawn_notice return nil unless object.unpublishing&.withdrawal? diff --git a/app/presenters/content_type_resolver.rb b/app/presenters/content_type_resolver.rb new file mode 100644 index 0000000000..9db5081d43 --- /dev/null +++ b/app/presenters/content_type_resolver.rb @@ -0,0 +1,43 @@ +class Presenters::ContentTypeResolver + def initialize(content_type) + self.content_type = content_type + end + + def resolve(object) + case object + when Hash + resolve_hash(object) + when Array + resolve_array(object) + else + object + end + end + +private + + def resolve_hash(hash) + hash.inject({}) do |memo, (key, value)| + memo.merge(key => resolve(value)) + end + end + + def resolve_array(array) + if array.all? { |v| v.is_a?(Hash) } + array = array.map(&:symbolize_keys) + content_for_type = extract_content(array) + end + + if content_for_type + content_for_type[:content] + else + array.map { |e| resolve(e) } + end + end + + def extract_content(array) + array.detect { |h| h[:content_type] == content_type && h[:content] } + end + + attr_accessor :content_type +end diff --git a/spec/graphql/types/edition_type_spec.rb b/spec/graphql/types/edition_type_spec.rb index 490e72b7ed..ee02011dc5 100644 --- a/spec/graphql/types/edition_type_spec.rb +++ b/spec/graphql/types/edition_type_spec.rb @@ -30,4 +30,54 @@ end end end + + context "content types in the details" do + context "when the body is a string" do + it "returns the string" do + edition = create(:edition, details: { body: "some text" }) + + expect( + run_graphql_field( + "Edition.details", + edition, + )[:body], + ).to eq("some text") + end + end + + context "when there are multiple content types and one is html" do + it "returns the html" do + edition = create(:edition, details: { + body: [ + { content_type: "text/govspeak", content: "some text" }, + { content_type: "text/html", content: "

some other text

" }, + ], + }) + + expect( + run_graphql_field( + "Edition.details", + edition, + )[:body], + ).to eq("

some other text

") + end + end + + context "when there are multiple content types and none are html" do + it "converts the govspeak to html" do + edition = create(:edition, details: { + body: [ + { content_type: "text/govspeak", content: "some text" }, + ], + }) + + expect( + run_graphql_field( + "Edition.details", + edition, + )[:body], + ).to eq("

some text

\n") + end + end + end end diff --git a/spec/presenters/content_type_resolver_spec.rb b/spec/presenters/content_type_resolver_spec.rb new file mode 100644 index 0000000000..3b6913a73b --- /dev/null +++ b/spec/presenters/content_type_resolver_spec.rb @@ -0,0 +1,130 @@ +RSpec.describe Presenters::ContentTypeResolver do + subject { described_class.new("html") } + + it "inlines content of the specified content type" do + result = subject.resolve( + body: [ + { content_type: "html", content: "

body

" }, + { content_type: "text", content: "body" }, + ], + ) + + expect(result).to eq( + body: "

body

", + ) + end + + it "works for string keys as well as symbols" do + result = subject.resolve( + "body" => [ + { "content_type" => "html", "content" => "

body

" }, + { "content_type" => "text", "content" => "body" }, + ], + ) + + expect(result).to eq( + "body" => "

body

", + ) + end + + it "does not affect other fields" do + result = subject.resolve( + string: "string", + array: [], + number: 123, + ) + + expect(result).to eq( + string: "string", + array: [], + number: 123, + ) + end + + it "handles hashes with content types but no content field" do + result = subject.resolve([ + content_type: "application/pdf", + path: "some/document.pdf", + ]) + + expect(result).to eq([ + content_type: "application/pdf", + path: "some/document.pdf", + ]) + end + + it "recurses on nested hashes" do + result = subject.resolve( + details: { + foo: { + bar: { + content: [ + { content_type: "html", content: "

body

" }, + ], + }, + }, + }, + ) + + expect(result).to eq( + details: { + foo: { + bar: { + content: "

body

", + }, + }, + }, + ) + end + + it "recurses on nested arrays" do + result = subject.resolve( + paragraphs: [ + [ + [ + { + body: [ + { content_type: "html", content: "

body

" }, + ], + }, + ], + ], + ], + ) + + expect(result).to eq( + paragraphs: [ + [ + [ + { + body: "

body

", + }, + ], + ], + ], + ) + end + + it "doesn't resolve incomplete multi-type content" do + result = subject.resolve( + details: { + body: { + content: [ + { content: "

body

" }, + { content_type: "html" }, + ], + }, + }, + ) + expect(result).to eq( + details: { + body: { + content: [ + { content: "

body

" }, + { content_type: "html" }, + ], + }, + }, + ) + end +end