diff --git a/COPYRIGHT b/COPYRIGHT index 08f7c7b939c1..30d0c1cb7eb7 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -58,4 +58,4 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/COPYRIGHT_short b/COPYRIGHT_short index d4773d9e4760..0693c77611e6 100644 --- a/COPYRIGHT_short +++ b/COPYRIGHT_short @@ -20,6 +20,6 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. See COPYRIGHT and LICENSE files for more details. diff --git a/modules/backlogs/app/contracts/backlog_buckets/base_contract.rb b/modules/backlogs/app/contracts/backlog_buckets/base_contract.rb index e1f8d6d6aa9d..d006b91ecc36 100644 --- a/modules/backlogs/app/contracts/backlog_buckets/base_contract.rb +++ b/modules/backlogs/app/contracts/backlog_buckets/base_contract.rb @@ -23,7 +23,7 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # See COPYRIGHT and LICENSE files for more details. #++ diff --git a/modules/wikis/app/models/queries/wikis/page_links/filter/provider_filter.rb b/modules/wikis/app/models/queries/wikis/page_links/filter/provider_filter.rb new file mode 100644 index 000000000000..101be290e85a --- /dev/null +++ b/modules/wikis/app/models/queries/wikis/page_links/filter/provider_filter.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Queries + module Wikis + module PageLinks + module Filter + class ProviderFilter < Filters::Base + self.model = ::Wikis::PageLink + + def type = :list + + def human_name + ::Wikis::PageLink.human_attribute_name(name) + end + + def allowed_values + ::Wikis::Provider.enabled.pluck(:universal_identifier).map { |uid| [uid, uid] } + end + + def left_outer_joins = :provider + + def where + operator_strategy.sql_for_field(values, ::Wikis::Provider.table_name, "universal_identifier") + end + end + end + end + end +end diff --git a/modules/wikis/app/models/queries/wikis/page_links/page_link_query.rb b/modules/wikis/app/models/queries/wikis/page_links/page_link_query.rb new file mode 100644 index 000000000000..8b493e789fd1 --- /dev/null +++ b/modules/wikis/app/models/queries/wikis/page_links/page_link_query.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Queries + module Wikis + module PageLinks + class PageLinkQuery + include BaseQuery + include UnpersistedQuery + + def self.model + @model ||= ::Wikis::PageLink + end + + def default_scope + ::Wikis::PageLink.includes(:provider).all + end + end + end + end +end diff --git a/modules/wikis/app/services/wikis/page_link_metadata_service.rb b/modules/wikis/app/services/wikis/page_link_metadata_service.rb new file mode 100644 index 000000000000..41f531edb81f --- /dev/null +++ b/modules/wikis/app/services/wikis/page_link_metadata_service.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Wikis + class PageLinkMetadataService + # @param page_links [ActiveRecord::Relation] + def initialize(page_links) + @result = ServiceResult.success(errors: ActiveModel::Errors.new(self)) + @relation = page_links + end + + # @return [ServiceResult] + def call + metadata = relation.group_by(&:provider).flat_map do |provider, page_links| + build_inputs(page_links).filter_map do |input_data| + provider.resolve("queries.page_info").call(input_data).value_or(nil) + end + end + + @result.result = enrich_models(metadata) + @result + end + + private + + attr_reader :relation + + def build_inputs(page_links) + page_links.filter_map do |page_link| + Adapters::Input::PageInfo.build(identifier: page_link.identifier).value_or(nil) + end + end + + def enrich_models(metadata) + identifier_title_map = metadata.map { [it.identifier, it.title, it.provider.id] } + variable_placeholders = Array.new(identifier_title_map.size, "(?,?,?)").join(",") + join_string = <<~SQL.squish + LEFT JOIN (VALUES #{variable_placeholders}) AS metadata(identifier, title, provider_id) + ON metadata.identifier = wiki_page_links.identifier AND metadata.provider_id = wiki_page_links.provider_id + SQL + + join_expression = ActiveRecord::Base.sanitize_sql_array([join_string, *identifier_title_map.flatten]) + + relation.joins(join_expression).select("wiki_page_links.*, metadata.title as title") + end + end +end diff --git a/modules/wikis/app/services/wikis/page_link_service.rb b/modules/wikis/app/services/wikis/page_link_service.rb index 3445b22c038b..c99015a23c60 100644 --- a/modules/wikis/app/services/wikis/page_link_service.rb +++ b/modules/wikis/app/services/wikis/page_link_service.rb @@ -23,7 +23,7 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # See COPYRIGHT and LICENSE files for more details. #++ diff --git a/modules/wikis/config/locales/en.yml b/modules/wikis/config/locales/en.yml index 4ee55a28be5c..ad8a3c5320d0 100644 --- a/modules/wikis/config/locales/en.yml +++ b/modules/wikis/config/locales/en.yml @@ -2,6 +2,8 @@ en: activerecord: attributes: + wikis/page_link: + provider: Wiki Provider wikis/xwiki_provider: authentication_method: Authentication method authentication_methods: diff --git a/modules/wikis/lib/api/v3/page_links/page_link_collection_representer.rb b/modules/wikis/lib/api/v3/page_links/page_link_collection_representer.rb new file mode 100644 index 000000000000..15243476718b --- /dev/null +++ b/modules/wikis/lib/api/v3/page_links/page_link_collection_representer.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module API + module V3 + module PageLinks + class PageLinkCollectionRepresenter < Decorators::OffsetPaginatedCollection + def _type = "WikiPageLinkCollection" + end + end + end +end diff --git a/modules/wikis/lib/api/v3/page_links/page_link_representer.rb b/modules/wikis/lib/api/v3/page_links/page_link_representer.rb index a0c80d5f2a6a..aa0986b0bf24 100644 --- a/modules/wikis/lib/api/v3/page_links/page_link_representer.rb +++ b/modules/wikis/lib/api/v3/page_links/page_link_representer.rb @@ -31,6 +31,14 @@ module API module V3 module PageLinks + URN_INLINE_PAGE_LINK = "#{URN_PREFIX}wikiPageLinks:Inline".freeze + URN_RELATION_PAGE_LINK = "#{URN_PREFIX}wikiPageLinks:Relation".freeze + + URN_PAGE_LINK_TYPE = { + "Wikis::RelationPageLink" => URN_RELATION_PAGE_LINK, + "Wikis::InlinePageLink" => URN_INLINE_PAGE_LINK + }.freeze + class PageLinkRepresenter < Decorators::Single include Decorators::LinkedResource include Decorators::DateProperty @@ -38,6 +46,7 @@ class PageLinkRepresenter < Decorators::Single property :id property :identifier + property :wiki_page_link_type, exec_context: :decorator date_time_property :created_at date_time_property :updated_at @@ -61,7 +70,9 @@ class PageLinkRepresenter < Decorators::Single } end - associated_resource :provider, v3_path: :wiki_provider + associated_resource :provider, v3_path: :wiki_provider, link: ->(*) { + { href: api_v3_paths.wiki_provider(represented.provider.universal_identifier), title: represented.provider.name } + } # TODO: Make this truly polymorphic - @mereghost 2026-04-13 associated_resource :linkable, @@ -69,7 +80,9 @@ class PageLinkRepresenter < Decorators::Single representer: ::API::V3::WorkPackages::WorkPackageRepresenter, skip_render: ->(*) { represented.linkable_id.nil? || represented.linkable_type != "WorkPackage" } - def _type = represented.class.name.demodulize + def _type = "WikiPageLink" + + def wiki_page_link_type = URN_PAGE_LINK_TYPE[represented.class.name] private diff --git a/modules/wikis/lib/api/v3/page_links/work_package_wiki_page_links_api.rb b/modules/wikis/lib/api/v3/page_links/work_package_wiki_page_links_api.rb new file mode 100644 index 000000000000..30dc11451e64 --- /dev/null +++ b/modules/wikis/lib/api/v3/page_links/work_package_wiki_page_links_api.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module API + module V3 + module PageLinks + class WorkPackageWikiPageLinksAPI < OpenProjectAPI + helpers do + def enrich_models_with_wiki_metadata(relation) + Wikis::PageLinkMetadataService.new(relation).call + end + end + + resources :wiki_page_links do + get do + query = ParamsToQueryService.new( + ::Wikis::PageLink, + current_user, + query_class: ::Queries::Wikis::PageLinks::PageLinkQuery + ).call(params) + + unless query.valid? + message = I18n.t("api_v3.errors.missing_or_malformed_parameter", parameter: "filters") + raise ::API::Errors::InvalidQuery.new(message) + end + + relation = query.results.where(linkable: @work_package) + + PageLinkCollectionRepresenter.new( + enrich_models_with_wiki_metadata(relation).result, + per_page: params[:pageSize], + self_link: api_v3_paths.work_package_page_links(@work_package.id), + current_user: + ) + end + end + end + end + end +end diff --git a/modules/wikis/lib/api/v3/providers/provider_representer.rb b/modules/wikis/lib/api/v3/providers/provider_representer.rb index 4dfaee7065ba..bd29de35de33 100644 --- a/modules/wikis/lib/api/v3/providers/provider_representer.rb +++ b/modules/wikis/lib/api/v3/providers/provider_representer.rb @@ -35,13 +35,13 @@ class ProviderRepresenter < Decorators::Single include Decorators::LinkedResource include Decorators::DateProperty - property :id + property :universal_identifier property :name date_time_property :created_at date_time_property :updated_at - self_link(path: :wiki_provider) + self_link(path: :wiki_provider, id_attribute: :universal_identifier) end end end diff --git a/modules/wikis/lib/open_project/wikis/engine.rb b/modules/wikis/lib/open_project/wikis/engine.rb index ecd3a452579e..aa9cd4e8ce81 100644 --- a/modules/wikis/lib/open_project/wikis/engine.rb +++ b/modules/wikis/lib/open_project/wikis/engine.rb @@ -60,6 +60,11 @@ class Engine < ::Rails::Engine ) OpenProject::TextFormatting::Filters::PatternMatcherFilter.append_matcher ::Wikis::TextFormatting::WikiLinkMatcher + + # Registering queries and filters + ::Queries::Register.register(::Queries::Wikis::PageLinks::PageLinkQuery) do + filter ::Queries::Wikis::PageLinks::Filter::ProviderFilter + end end replace_principal_references "Wikis::PageLink" => %i[author_id] @@ -99,5 +104,10 @@ class Engine < ::Rails::Engine add_api_path(:wiki_page_link) { |page_link_id| "#{root}/wiki_page_links/#{page_link_id}" } add_api_path(:wiki_provider) { |provider_id| "#{root}/wiki_providers/#{provider_id}" } + add_api_path(:work_package_page_links) { |work_package_id| "#{work_package(work_package_id)}/wiki_page_links" } + + add_api_endpoint "API::V3::WorkPackages::WorkPackagesAPI", :id do + mount ::API::V3::PageLinks::WorkPackageWikiPageLinksAPI + end end end diff --git a/modules/wikis/spec/lib/api/v3/page_links/page_link_representer_spec.rb b/modules/wikis/spec/lib/api/v3/page_links/page_link_representer_spec.rb index d915b5247130..6457e66a8a2b 100644 --- a/modules/wikis/spec/lib/api/v3/page_links/page_link_representer_spec.rb +++ b/modules/wikis/spec/lib/api/v3/page_links/page_link_representer_spec.rb @@ -60,7 +60,7 @@ module PageLinks describe "provider" do it_behaves_like "has a titled link" do let(:link) { "provider" } - let(:href) { "/api/v3/wiki_providers/#{represented.provider_id}" } + let(:href) { "/api/v3/wiki_providers/#{represented.provider.universal_identifier}" } let(:title) { represented.provider.name } end end @@ -113,18 +113,34 @@ module PageLinks end describe "properties" do + describe "wiki_page_link_type" do + context "when InlinePageLink" do + let(:represented) { inline_page_link } + + it_behaves_like "property", :wikiPageLinkType do + let(:value) { URN_INLINE_PAGE_LINK } + end + end + + context "when RelationPageLink" do + it_behaves_like "property", :wikiPageLinkType do + let(:value) { URN_RELATION_PAGE_LINK } + end + end + end + describe "_type" do context "when InlinePageLink" do let(:represented) { inline_page_link } it_behaves_like "property", :_type do - let(:value) { "InlinePageLink" } + let(:value) { "WikiPageLink" } end end context "when RelationPageLink" do it_behaves_like "property", :_type do - let(:value) { "RelationPageLink" } + let(:value) { "WikiPageLink" } end end end diff --git a/modules/wikis/spec/lib/api/v3/providers/provider_representer_spec.rb b/modules/wikis/spec/lib/api/v3/providers/provider_representer_spec.rb index 602bae7b9635..80fcaa18f4c9 100644 --- a/modules/wikis/spec/lib/api/v3/providers/provider_representer_spec.rb +++ b/modules/wikis/spec/lib/api/v3/providers/provider_representer_spec.rb @@ -48,13 +48,21 @@ module Providers describe "self" do it_behaves_like "has a titled link" do let(:link) { "self" } - let(:href) { "/api/v3/wiki_providers/#{represented.id}" } + let(:href) { "/api/v3/wiki_providers/#{represented.universal_identifier}" } let(:title) { represented.name } end end end describe "properties" do + it_behaves_like "property", :name do + let(:value) { represented.name } + end + + it_behaves_like "property", :universalIdentifier do + let(:value) { represented.universal_identifier } + end + it_behaves_like "datetime property", :createdAt do let(:value) { represented.created_at } end diff --git a/modules/wikis/spec/requests/api/v3/page_links/page_links_api_spec.rb b/modules/wikis/spec/requests/api/v3/page_links/page_links_api_spec.rb new file mode 100644 index 000000000000..ccd224e09778 --- /dev/null +++ b/modules/wikis/spec/requests/api/v3/page_links/page_links_api_spec.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require "spec_helper" +require_module_spec_helper + +RSpec.describe "API v3 wiki page links resource" do + include API::V3::Utilities::PathHelper + + let(:work_package) { create(:work_package) } + let(:internal_wiki) { create(:internal_wiki_provider) } + let(:xwiki_provider) { create(:xwiki_provider) } + + let(:project) { work_package.project } + + let(:user) { create(:user, member_with_permissions: { project => %i(view_work_packages) }) } + + let(:relation_page_links) { create_list(:relation_wiki_page_link, 3, provider: xwiki_provider, linkable: work_package) } + let(:inline_page_links) { create_list(:inline_wiki_page_link, 3, provider: internal_wiki, linkable: work_package) } + + let(:unrelated_page_links) do + create_list(:inline_wiki_page_link, 3, provider: internal_wiki, linkable: create(:work_package, project: project)) + end + + before do + login_as user + stub_provider_queries + unrelated_page_links + end + + describe "GET /api/v3/work_packages/:id/wiki_page_links" do + let(:path) { api_v3_paths.work_package_page_links(work_package.id) } + + context "with all preconditions met (happy path)" do + before { get path } + + it_behaves_like "API V3 collection response", 6, 6, "WikiPageLink", "WikiPageLinkCollection" do + let(:elements) { Wikis::PageLink.where(linkable: work_package).order(id: :desc).all } + end + end + + context "when filtered by provider" do + let(:filter) { [{ provider: { operator: "=", values: [internal_wiki.universal_identifier] } }] } + + before do + get "#{path}?filters=#{CGI.escape(filter.to_json)}" + end + + it_behaves_like "API V3 collection response", 3, 3, "WikiPageLink", "WikiPageLinkCollection" do + let(:elements) { Wikis::PageLink.where(linkable: work_package, provider: internal_wiki).order(id: :desc).all } + end + end + end + + private + + def stub_provider_queries + internal_class = class_double(Wikis::Adapters::Providers::Internal::Queries::PageInfo) + xwiki_class = class_double(Wikis::Adapters::Providers::XWiki::Queries::PageInfo) + + internal_query = instance_double(Wikis::Adapters::Providers::Internal::Queries::PageInfo) + xwiki_query = instance_double(Wikis::Adapters::Providers::XWiki::Queries::PageInfo) + + Wikis::Adapters::Registry.stub("internal.queries.page_info", internal_class) + Wikis::Adapters::Registry.stub("xwiki.queries.page_info", xwiki_class) + + allow(internal_class).to receive(:new).and_return(internal_query) + allow(xwiki_class).to receive(:new).and_return(xwiki_query) + stub_query_calls(inline_page_links, internal_query) + stub_query_calls(relation_page_links, xwiki_query) + end + + def stub_query_calls(links, query) + links.each do |link| + Wikis::Adapters::Input::PageInfo.build(identifier: link.identifier).bind do |input| + allow(query).to receive(:call).with(input).and_return(Success(build_page_info(link))) + end + end + end + + def build_page_info(link) + Wikis::Adapters::Results::PageInfo.new( + identifier: link.identifier, href: "valid_uri", title: "Title of #{link.identifier}", provider: link.provider + ) + end +end diff --git a/modules/wikis/spec/services/wikis/page_link_metadata_service_spec.rb b/modules/wikis/spec/services/wikis/page_link_metadata_service_spec.rb new file mode 100644 index 000000000000..d55a37179035 --- /dev/null +++ b/modules/wikis/spec/services/wikis/page_link_metadata_service_spec.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require "spec_helper" +require_module_spec_helper + +module Wikis + RSpec.describe PageLinkMetadataService do + let(:relation) { PageLink.limit(30) } + let(:query_double) { instance_double(Adapters::Providers::Internal::Queries::PageInfo) } + let(:query_class_double) { class_double(Adapters::Providers::Internal::Queries::PageInfo) } + + shared_let(:provider) { create(:internal_wiki_provider) } + shared_let(:page_links) { create_list(:relation_wiki_page_link, 3, provider:) } + + subject(:service) { described_class.new(relation) } + + before do + allow(query_class_double).to receive(:new).with(model: provider).and_return(query_double) + Adapters::Registry.stub("internal.queries.page_info", query_class_double) + + build_inputs.each do |input| + allow(query_double).to receive(:call).with(input).and_return( + Success( + Adapters::Results::PageInfo.new(title: "Wikis, now with more cheese! Part #{input.identifier}", + identifier: input.identifier, + href: "totally_valid_url", + provider:) + ) + ) + end + end + + it "returns a new relation" do + service_result = service.call + + expect(service_result).to be_success + expect(service_result.errors).to be_empty + expect(service_result.result).to be_an(ActiveRecord::Relation) + end + + it "adds the title attribute to the metadata association" do + service_result = service.call + expect(service_result).to be_success + + page_links = service_result.result + expect(page_links.first.title).to eq("Wikis, now with more cheese! Part #{page_links.first.identifier}") + end + + context "when page links have the same identifier but different providers" do + shared_let(:xwiki_provider) { create(:xwiki_provider) } + let(:new_page_links) do + page_links.map do |pl| + create(:relation_wiki_page_link, provider: xwiki_provider, identifier: pl.identifier) + end + end + + before do + new_double = instance_double(Adapters::Providers::Internal::Queries::PageInfo) + allow(query_class_double).to receive(:new).with(model: xwiki_provider).and_return(new_double) + Adapters::Registry.stub("xwiki.queries.page_info", query_class_double) + + new_page_links.map do |pl| + input = Adapters::Input::PageInfo.build(identifier: pl.identifier).value_or(nil) + allow(new_double).to receive(:call).with(input).and_return( + Success( + Adapters::Results::PageInfo.new(title: "Wikis, now with more cheese! Part #{pl.id}", + identifier: input.identifier, + href: "totally_valid_url", + provider: xwiki_provider) + ) + ) + end + end + + it "maps the titles to the correct page link" do + service_result = service.call + + expect(service_result).to be_success + page_links = service_result.result + + page_links.find_each do |page_link| + case page_link.provider_id + when xwiki_provider.id + expect(page_link.title).to eq("Wikis, now with more cheese! Part #{page_link.id}") + else + expect(page_link.title).to eq("Wikis, now with more cheese! Part #{page_link.identifier}") + end + end + end + end + + private + + def build_inputs(relation = page_links) + relation.filter_map { Adapters::Input::PageInfo.build(identifier: it.identifier).value_or(nil) } + end + end +end diff --git a/modules/wikis/spec/spec_helper.rb b/modules/wikis/spec/spec_helper.rb new file mode 100644 index 000000000000..72f308d2e316 --- /dev/null +++ b/modules/wikis/spec/spec_helper.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require "dry/container/stub" +require "dry/monads" + +RSpec.configure do |config| + config.include Dry::Monads[:result] + + config.prepend_before do + Wikis::Adapters::Registry.enable_stubs! + end + config.append_after do + Wikis::Adapters::Registry.unstub + end +end