From ddb8e95f4362cfdcdb0dbf2147127ba08d0b3edd Mon Sep 17 00:00:00 2001 From: Bruce Bolt Date: Mon, 30 Dec 2024 11:00:37 +0000 Subject: [PATCH] Add support for reverse link set links in GraphQL This adds support for reverse links that are part of a link set. --- .../reverse_linked_to_editions_source.rb | 29 +++++++++++++++++++ app/graphql/types/base_object.rb | 9 ++++++ app/graphql/types/edition_type.rb | 22 ++++++++++++++ .../reverse_linked_to_editions_source_spec.rb | 20 +++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 app/graphql/sources/reverse_linked_to_editions_source.rb create mode 100644 spec/graphql/sources/reverse_linked_to_editions_source_spec.rb diff --git a/app/graphql/sources/reverse_linked_to_editions_source.rb b/app/graphql/sources/reverse_linked_to_editions_source.rb new file mode 100644 index 000000000..1f5c628fa --- /dev/null +++ b/app/graphql/sources/reverse_linked_to_editions_source.rb @@ -0,0 +1,29 @@ +module Sources + class ReverseLinkedToEditionsSource < GraphQL::Dataloader::Source + # rubocop:disable Lint/MissingSuper + def initialize(parent_object:) + @object = parent_object + @content_store = parent_object.content_store.to_sym + end + # rubocop:enable Lint/MissingSuper + + def fetch(link_types) + all_links = @object + .document + .reverse_links + .where(link_type: link_types) + + link_types_map = link_types.index_with { [] } + + all_links.each_with_object(link_types_map) { |link, hash| + hash[link.link_type].concat(editions_for_link(link)) + }.values + end + + private + + def editions_for_link(link) + link.source_documents.map { |document| document.send(@content_store) } + end + end +end diff --git a/app/graphql/types/base_object.rb b/app/graphql/types/base_object.rb index e6c92d6fb..5b52b28fd 100644 --- a/app/graphql/types/base_object.rb +++ b/app/graphql/types/base_object.rb @@ -14,5 +14,14 @@ def self.links_field(field_name_and_link_type, graphql_field_type) .load(field_name_and_link_type.to_s) end end + + def self.reverse_links_field(field_name_and_link_type, belongs_to, graphql_field_type) + field(field_name_and_link_type.to_sym, graphql_field_type) + + define_method(field_name_and_link_type.to_sym) do + dataloader.with(Sources::ReverseLinkedToEditionsSource, parent_object: object) + .load(belongs_to.to_s) + end + end end end diff --git a/app/graphql/types/edition_type.rb b/app/graphql/types/edition_type.rb index 88e216d1a..f8346f638 100644 --- a/app/graphql/types/edition_type.rb +++ b/app/graphql/types/edition_type.rb @@ -89,6 +89,28 @@ class EditionLinks < Types::BaseObject links_field :worldwide_organisation, [EditionType] links_field :worldwide_organisations, [EditionType] + reverse_links_field :child_taxons, :parent_taxons, [EditionType] + reverse_links_field :children, :parent, [EditionType] + reverse_links_field :document_collections, :documents, [EditionType] + reverse_links_field :level_one_taxons, :root_taxon, [EditionType] + reverse_links_field :ministers, :ministerial, [EditionType] + reverse_links_field :part_of_step_navs, :pages_part_of_step_nav, [EditionType] + reverse_links_field :policies, :working_groups, [EditionType] + reverse_links_field :related_to_step_navs, :pages_related_to_step_nav, [EditionType] + reverse_links_field :secondary_to_step_navs, :secondary_to_step_navs, [EditionType] + + field :role_appointments, [EditionType] + + def role_appointments + if object.document_type == "role" + dataloader.with(Sources::ReverseLinkedToEditionsSource, parent_object: object) + .load("role") + else + dataloader.with(Sources::ReverseLinkedToEditionsSource, parent_object: object) + .load("person") + end + end + def available_translations Presenters::Queries::AvailableTranslations.by_edition(object) .translations.fetch(:available_translations, []) diff --git a/spec/graphql/sources/reverse_linked_to_editions_source_spec.rb b/spec/graphql/sources/reverse_linked_to_editions_source_spec.rb new file mode 100644 index 000000000..0191b8bef --- /dev/null +++ b/spec/graphql/sources/reverse_linked_to_editions_source_spec.rb @@ -0,0 +1,20 @@ +RSpec.describe Sources::ReverseLinkedToEditionsSource do + it "returns the specified reverse link set links" do + target_document = create(:edition) + source_document_1 = create(:edition) + source_document_2 = create(:edition) + source_document_3 = create(:edition) + link_set_1 = create(:link_set, content_id: source_document_1.content_id) + link_set_2 = create(:link_set, content_id: source_document_2.content_id) + link_set_3 = create(:link_set, content_id: source_document_3.content_id) + create(:link, link_set: link_set_1, target_content_id: target_document.content_id, link_type: "test_link") + create(:link, link_set: link_set_2, target_content_id: target_document.content_id, link_type: "another_link_type") + create(:link, link_set: link_set_3, target_content_id: target_document.content_id, link_type: "test_link") + + GraphQL::Dataloader.with_dataloading do |dataloader| + request = dataloader.with(described_class, parent_object: target_document).request("test_link") + + expect(request.load).to eq([source_document_1, source_document_3]) + end + end +end