From 1ba2bf95ceb74b481f6d287f3a85e2f07c9a6c7d Mon Sep 17 00:00:00 2001 From: Jan Sandbrink Date: Thu, 4 Jun 2026 11:03:45 +0200 Subject: [PATCH] Use stable identifiers to fetch XWiki pages We now have two ways to fetch page infos from XWiki: * using the canonical identifier (but through a new endpoint that will resolve stable identifiers as well) * using the stable identifier Since we want to store the stable identifier, the stable version is the one exposed through the XWiki registry. The canonical form is required to fully resolve some list queries from XWiki properly. --- .../xwiki/canonical_page_reference.rb | 4 + .../xwiki/queries/canonical_page_info.rb | 76 ++++++++++++ .../providers/xwiki/queries/search_pages.rb | 8 +- .../{page_info.rb => stable_page_info.rb} | 11 +- .../adapters/providers/xwiki/registry.rb | 2 +- .../providers/xwiki/stable_page_reference.rb | 60 ++++++++++ .../api/v3/page_links/page_links_api_spec.rb | 4 +- .../xwiki/canonical_page_reference_spec.rb | 20 ++++ ...fo_spec.rb => canonical_page_info_spec.rb} | 52 ++++----- .../xwiki/queries/search_pages_spec.rb | 7 ++ .../xwiki/queries/stable_page_info_spec.rb | 110 ++++++++++++++++++ .../xwiki/stable_page_reference_spec.rb | 66 +++++++++++ .../xwiki/canonical_page_info.yml | 73 ++++++++++++ .../vcr_cassettes/xwiki/query_exact_match.yml | 48 ++++---- .../vcr_cassettes/xwiki/query_no_match.yml | 8 +- .../xwiki/query_partial_match.yml | 46 ++++---- .../xwiki/query_quoted_match.yml | 47 ++++---- .../xwiki/query_unquoted_match.yml | 47 ++++---- .../vcr_cassettes/xwiki/stable_page_info.yml | 50 ++++++++ 19 files changed, 612 insertions(+), 127 deletions(-) create mode 100644 modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/canonical_page_info.rb rename modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/{page_info.rb => stable_page_info.rb} (84%) create mode 100644 modules/wikis/app/services/wikis/adapters/providers/xwiki/stable_page_reference.rb rename modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/{page_info_spec.rb => canonical_page_info_spec.rb} (71%) create mode 100644 modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/stable_page_info_spec.rb create mode 100644 modules/wikis/spec/services/wikis/adapters/providers/xwiki/stable_page_reference_spec.rb create mode 100644 spec/support/fixtures/vcr_cassettes/xwiki/canonical_page_info.yml create mode 100644 spec/support/fixtures/vcr_cassettes/xwiki/stable_page_info.yml diff --git a/modules/wikis/app/services/wikis/adapters/providers/xwiki/canonical_page_reference.rb b/modules/wikis/app/services/wikis/adapters/providers/xwiki/canonical_page_reference.rb index 431360280c5..6ff8767b2e1 100644 --- a/modules/wikis/app/services/wikis/adapters/providers/xwiki/canonical_page_reference.rb +++ b/modules/wikis/app/services/wikis/adapters/providers/xwiki/canonical_page_reference.rb @@ -51,6 +51,10 @@ module Wikis spaces_path = spaces.map { "/spaces/#{CGI.escapeURIComponent(it)}" }.join "/wikis/#{CGI.escapeURIComponent(wiki)}#{spaces_path}/pages/#{CGI.escapeURIComponent(page)}" end + + def to_s + "#{wiki}:#{spaces.join('.')}.#{page}" + end end end end diff --git a/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/canonical_page_info.rb b/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/canonical_page_info.rb new file mode 100644 index 00000000000..1274ddf8f39 --- /dev/null +++ b/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/canonical_page_info.rb @@ -0,0 +1,76 @@ +# 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 + module Adapters + module Providers + module XWiki + module Queries + # Fetch page information using a canonical XWiki identifier + class CanonicalPageInfo < BaseQuery + include Concerns::XWikiQuery + + def call(input_data:, auth_strategy:) + ref = CanonicalPageReference.parse(input_data.identifier) + return failure(code: :not_found) unless ref + + perform_request(ref, auth_strategy:) do |data| + success( + Results::PageInfo.new( + identifier: StablePageReference.parse(data.fetch("id")).to_s, + title: data.fetch("title"), + href: data.fetch("xwikiAbsoluteUrl"), + provider: + ) + ) + end + end + + def perform_request(reference, auth_strategy:, &) + authenticated(auth_strategy) do |http| + handle_response( + # This query is implemented as a PUT on the XWiki side, because it dynamically creates the + # stable identifier that it returns. Passing an empty JSON body is also required for the + # endpoint to not raise an error 🤷 + http.with(headers: { "Content-Type": "application/json" }) + .put( + rest_url("openproject/documents", query: { docRef: reference.to_s }), + body: "{}" + ), + & + ) + end + end + end + end + end + end + end +end diff --git a/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/search_pages.rb b/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/search_pages.rb index 5c09cabb327..c8a7e5122d5 100644 --- a/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/search_pages.rb +++ b/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/search_pages.rb @@ -47,7 +47,7 @@ module Wikis json.fetch("searchResults") .uniq { |r| r.fetch("id") } .map do |r| - result = page_info(identifier: r.fetch("id"), auth_strategy:) + result = canonical_page_info(identifier: r.fetch("id"), auth_strategy:) return result if result.failure? result.value! @@ -62,6 +62,12 @@ module Wikis def escape_quotes(string) string.gsub("\\", "\\\\").gsub('"', '\"') end + + def canonical_page_info(identifier:, auth_strategy:) + Input::PageInfo.build(identifier:).bind do |input_data| + CanonicalPageInfo.new(model: provider).call(input_data:, auth_strategy:) + end + end end end end diff --git a/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/page_info.rb b/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/stable_page_info.rb similarity index 84% rename from modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/page_info.rb rename to modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/stable_page_info.rb index db320cefd8e..51d2adbff3a 100644 --- a/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/page_info.rb +++ b/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/stable_page_info.rb @@ -33,20 +33,21 @@ module Wikis module Providers module XWiki module Queries - class PageInfo < BaseQuery + # Fetch page information using a stable XWiki identifier + class StablePageInfo < BaseQuery include Concerns::XWikiQuery def call(input_data:, auth_strategy:) - ref = CanonicalPageReference.parse(input_data.identifier) + ref = StablePageReference.parse(input_data.identifier) return failure(code: :not_found) unless ref authenticated(auth_strategy) do |http| handle_response(http.get(rest_url(ref.rest_path))) do |data| success( Results::PageInfo.new( - identifier: input_data.identifier, - title: data["title"], - href: data["xwikiAbsoluteUrl"], + identifier: ref.to_s, + title: data.fetch("title"), + href: data.fetch("xwikiAbsoluteUrl"), provider: ) ) diff --git a/modules/wikis/app/services/wikis/adapters/providers/xwiki/registry.rb b/modules/wikis/app/services/wikis/adapters/providers/xwiki/registry.rb index 534328ecf15..057136c3ade 100644 --- a/modules/wikis/app/services/wikis/adapters/providers/xwiki/registry.rb +++ b/modules/wikis/app/services/wikis/adapters/providers/xwiki/registry.rb @@ -61,7 +61,7 @@ module Wikis namespace("queries") do register(:user, Queries::User) - register(:page_info, Queries::PageInfo) + register(:page_info, Queries::StablePageInfo) register(:referencing_pages, Queries::ReferencingPages) register(:relation_page_links, Queries::RelationPageLinks) register(:search_pages, Queries::SearchPages) diff --git a/modules/wikis/app/services/wikis/adapters/providers/xwiki/stable_page_reference.rb b/modules/wikis/app/services/wikis/adapters/providers/xwiki/stable_page_reference.rb new file mode 100644 index 00000000000..d92675cf8cc --- /dev/null +++ b/modules/wikis/app/services/wikis/adapters/providers/xwiki/stable_page_reference.rb @@ -0,0 +1,60 @@ +# 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 + module Adapters + module Providers + module XWiki + # Represents an XWiki page identifier using the "stable" page identifier format: e.g. 0b89a + StablePageReference = Data.define(:uid) do + class << self + private :new + + def parse(uid) + return nil if uid.nil? + + new(uid:) + end + end + + # Maps the reference to the REST API path, excluding the `/rest` prefix, e.g. + # /openproject/documents/0b89a + def rest_path + "/openproject/documents/#{CGI.escapeURIComponent(uid)}" + end + + def to_s + uid + end + end + end + end + end +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 index 3ba6cb3f773..9f16d377916 100644 --- 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 @@ -168,10 +168,10 @@ RSpec.describe "API v3 wiki page links resource", content_type: :json do def stub_provider_queries internal_class = class_double(Wikis::Adapters::Providers::Internal::Queries::PageInfo) - xwiki_class = class_double(Wikis::Adapters::Providers::XWiki::Queries::PageInfo) + xwiki_class = class_double(Wikis::Adapters::Providers::XWiki::Queries::StablePageInfo) internal_query = instance_double(Wikis::Adapters::Providers::Internal::Queries::PageInfo) - xwiki_query = instance_double(Wikis::Adapters::Providers::XWiki::Queries::PageInfo) + xwiki_query = instance_double(Wikis::Adapters::Providers::XWiki::Queries::StablePageInfo) Wikis::Adapters::Registry.stub("internal.queries.page_info", internal_class) Wikis::Adapters::Registry.stub("xwiki.queries.page_info", xwiki_class) diff --git a/modules/wikis/spec/services/wikis/adapters/providers/xwiki/canonical_page_reference_spec.rb b/modules/wikis/spec/services/wikis/adapters/providers/xwiki/canonical_page_reference_spec.rb index 5ecbf7daa0f..8c7d29619a2 100644 --- a/modules/wikis/spec/services/wikis/adapters/providers/xwiki/canonical_page_reference_spec.rb +++ b/modules/wikis/spec/services/wikis/adapters/providers/xwiki/canonical_page_reference_spec.rb @@ -87,4 +87,24 @@ RSpec.describe Wikis::Adapters::Providers::XWiki::CanonicalPageReference do it { is_expected.to eq("/wikis/xwiki/spaces/My%20Space/pages/My%20Page") } end end + + describe "#to_s" do + subject { described_class.parse(identifier).to_s } + + context "with a standard identifier" do + let(:identifier) { "xwiki:Main.WebHome" } + + it "roundtrips" do + expect(subject).to eq(identifier) + end + end + + context "with a nested space identifier" do + let(:identifier) { "xwiki:MySpace.SubSpace.PageName" } + + it "roundtrips" do + expect(subject).to eq(identifier) + end + end + end end diff --git a/modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/page_info_spec.rb b/modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/canonical_page_info_spec.rb similarity index 71% rename from modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/page_info_spec.rb rename to modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/canonical_page_info_spec.rb index e9be445be71..27e913c3a2b 100644 --- a/modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/page_info_spec.rb +++ b/modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/canonical_page_info_spec.rb @@ -31,52 +31,46 @@ require "spec_helper" require_module_spec_helper -RSpec.describe Wikis::Adapters::Providers::XWiki::Queries::PageInfo, :webmock do - it "is registered" do - expect(Wikis::Adapters::Registry.resolve("xwiki.queries.page_info")).to eq(described_class) +RSpec.describe Wikis::Adapters::Providers::XWiki::Queries::CanonicalPageInfo, :webmock do + it "is not registered" do + expect(Wikis::Adapters::Registry.resolve("xwiki.queries.page_info")).not_to eq(described_class) end describe "#call" do let(:user) { create(:user) } - let(:wiki_provider) { create(:xwiki_provider, :with_connected_user, url: "https://xwiki.example.com/", connected_user: user) } + let(:wiki_provider) { create(:xwiki_provider, :for_local_connection, connected_user: user) } let(:identifier) { "xwiki:Main.WebHome" } - let(:page_url) { "https://xwiki.example.com/rest/wikis/xwiki/spaces/Main/pages/WebHome" } + let(:page_url) { "https://xwiki.local/rest/openproject/documents?docRef=xwiki:Main.WebHome" } let(:auth_strategy) { Wikis::Adapters::Input::AuthStrategy.build(key: :bearer_token, user:, provider: wiki_provider).value! } let(:input_data) { Wikis::Adapters::Input::PageInfo.build(identifier:).value! } let(:query) { described_class.new(model: wiki_provider) } subject(:result) { query.call(input_data:, auth_strategy:) } - context "when the page exists" do - let(:page_response) do - { "title" => "Home", "xwikiAbsoluteUrl" => "https://xwiki.example.com/bin/view/Main/" }.to_json - end - - before do - stub_request(:get, page_url) - .with(headers: { "Authorization" => "Bearer user-bearer-token" }) - .to_return(status: 200, body: page_response, headers: { "Content-Type" => "application/json" }) - end + context "when the page exists", vcr: "xwiki/canonical_page_info" do + # Set the expected identifier according to the stable identifier returned by XWiki (or update the VCR cassette accordingly) + let(:expected_identifier) { "484f4" } it "returns Success with title and href" do expect(result).to be_success expect(result.value!).to have_attributes( - identifier:, + identifier: expected_identifier, title: "Home", - href: "https://xwiki.example.com/bin/view/Main/" + href: "https://xwiki.local/bin/view/Main/" ) end end context "with a nested space identifier" do let(:identifier) { "xwiki:MySpace.SubSpace.PageName" } - let(:page_url) { "https://xwiki.example.com/rest/wikis/xwiki/spaces/MySpace/spaces/SubSpace/pages/PageName" } - let(:absolute_url) { "https://xwiki.example.com/bin/view/MySpace/SubSpace/PageName" } + let(:page_url) do + "https://xwiki.local/rest/openproject/documents?docRef=xwiki:MySpace.SubSpace.PageName" + end + let(:absolute_url) { "https://xwiki.local/bin/view/MySpace/SubSpace/PageName" } before do - stub_request(:get, page_url) - .to_return(status: 200, body: { "title" => "Nested Page", - "xwikiAbsoluteUrl" => absolute_url }.to_json, + stub_request(:put, page_url) + .to_return(status: 200, body: { id: "foo", title: "Nested Page", xwikiAbsoluteUrl: absolute_url }.to_json, headers: { "Content-Type" => "application/json" }) end @@ -93,44 +87,44 @@ RSpec.describe Wikis::Adapters::Providers::XWiki::Queries::PageInfo, :webmock do end context "when no OAuth token exists for the user" do - let(:wiki_provider) { create(:xwiki_provider, :with_oauth_client, url: "https://xwiki.example.com/") } + let(:wiki_provider) { create(:xwiki_provider, :with_oauth_client, url: "https://xwiki.local/") } it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :missing_token)) } end context "when the page is not found" do - before { stub_request(:get, page_url).to_return(status: 404, body: "") } + before { stub_request(:put, page_url).to_return(status: 404, body: "") } it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :not_found)) } end context "when access is unauthorized" do - before { stub_request(:get, page_url).to_return(status: 401, body: "") } + before { stub_request(:put, page_url).to_return(status: 401, body: "") } it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :unauthorized)) } end context "when access is forbidden" do - before { stub_request(:get, page_url).to_return(status: 403, body: "") } + before { stub_request(:put, page_url).to_return(status: 403, body: "") } it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :unauthorized)) } end context "when XWiki returns a non-2xx status" do - before { stub_request(:get, page_url).to_return(status: 500, body: "Internal Server Error") } + before { stub_request(:put, page_url).to_return(status: 500, body: "Internal Server Error") } it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :request_failed)) } end context "when a network error occurs" do - before { stub_request(:get, page_url).to_timeout } + before { stub_request(:put, page_url).to_timeout } it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :connection_error)) } end context "when the response body is not valid JSON" do before do - stub_request(:get, page_url) + stub_request(:put, page_url) .to_return(status: 200, body: "not json", headers: { "Content-Type" => "application/json" }) end diff --git a/modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/search_pages_spec.rb b/modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/search_pages_spec.rb index 9ae5ed65751..590fb2625f6 100644 --- a/modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/search_pages_spec.rb +++ b/modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/search_pages_spec.rb @@ -53,6 +53,13 @@ RSpec.describe Wikis::Adapters::Providers::XWiki::Queries::SearchPages, :disable expect(subject.value!.first.title).to eq("Test Page for RSpec") end + it "returns a complete PageInfo result" do + page_info = subject.value!.first + page_info.to_h.each do |attribute, value| + expect(value).not_to be_nil, "#{attribute} was expected to be non-nil, but was nil" + end + end + it "returns no other random results" do expect(subject.value!.count).to eq(1) end diff --git a/modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/stable_page_info_spec.rb b/modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/stable_page_info_spec.rb new file mode 100644 index 00000000000..85814a11e1b --- /dev/null +++ b/modules/wikis/spec/services/wikis/adapters/providers/xwiki/queries/stable_page_info_spec.rb @@ -0,0 +1,110 @@ +# 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 Wikis::Adapters::Providers::XWiki::Queries::StablePageInfo, :webmock do + it "is registered" do + expect(Wikis::Adapters::Registry.resolve("xwiki.queries.page_info")).to eq(described_class) + end + + describe "#call" do + let(:user) { create(:user) } + let(:wiki_provider) { create(:xwiki_provider, :for_local_connection, connected_user: user) } + let(:identifier) { "123abc" } + let(:page_url) { "https://xwiki.local/rest/openproject/documents/123abc" } + let(:auth_strategy) { Wikis::Adapters::Input::AuthStrategy.build(key: :bearer_token, user:, provider: wiki_provider).value! } + let(:input_data) { Wikis::Adapters::Input::PageInfo.build(identifier:).value! } + let(:query) { described_class.new(model: wiki_provider) } + + subject(:result) { query.call(input_data:, auth_strategy:) } + + context "when the page exists", vcr: "xwiki/stable_page_info" do + # Before recording cassette make sure a wiki page with title "Test Page for RSpec" exists + # Set the according identifier of that test page below + let(:identifier) { "d70f1" } + + it "returns Success with title and href" do + expect(result).to be_success + expect(result.value!).to have_attributes( + identifier: "d70f1", + title: "Test Page for RSpec", + href: "https://xwiki.local/bin/view/Test%20Page/" + ) + end + end + + context "when no OAuth token exists for the user" do + let(:wiki_provider) { create(:xwiki_provider, :with_oauth_client, url: "https://xwiki.local/") } + + it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :missing_token)) } + end + + context "when the page is not found" do + before { stub_request(:get, page_url).to_return(status: 404, body: "") } + + it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :not_found)) } + end + + context "when access is unauthorized" do + before { stub_request(:get, page_url).to_return(status: 401, body: "") } + + it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :unauthorized)) } + end + + context "when access is forbidden" do + before { stub_request(:get, page_url).to_return(status: 403, body: "") } + + it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :unauthorized)) } + end + + context "when XWiki returns a non-2xx status" do + before { stub_request(:get, page_url).to_return(status: 500, body: "Internal Server Error") } + + it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :request_failed)) } + end + + context "when a network error occurs" do + before { stub_request(:get, page_url).to_timeout } + + it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :connection_error)) } + end + + context "when the response body is not valid JSON" do + before do + stub_request(:get, page_url) + .to_return(status: 200, body: "not json", headers: { "Content-Type" => "application/json" }) + end + + it { is_expected.to be_failure.and have_attributes(failure: have_attributes(code: :invalid_response)) } + end + end +end diff --git a/modules/wikis/spec/services/wikis/adapters/providers/xwiki/stable_page_reference_spec.rb b/modules/wikis/spec/services/wikis/adapters/providers/xwiki/stable_page_reference_spec.rb new file mode 100644 index 00000000000..ee223d4af85 --- /dev/null +++ b/modules/wikis/spec/services/wikis/adapters/providers/xwiki/stable_page_reference_spec.rb @@ -0,0 +1,66 @@ +# 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 Wikis::Adapters::Providers::XWiki::StablePageReference do + describe ".parse" do + subject { described_class.parse(identifier) } + + let(:identifier) { "123abc" } + + it { is_expected.to have_attributes(uid: "123abc") } + + context "when passing nil" do + let(:identifier) { nil } + + it { is_expected.to be_nil } + end + end + + describe "#rest_path" do + subject { described_class.parse(identifier).rest_path } + + let(:identifier) { "123abc" } + + it { is_expected.to eq("/openproject/documents/123abc") } + end + + describe "#to_s" do + subject { described_class.parse(identifier).to_s } + + let(:identifier) { "123abc" } + + it "roundtrips" do + expect(subject).to eq(identifier) + end + end +end diff --git a/spec/support/fixtures/vcr_cassettes/xwiki/canonical_page_info.yml b/spec/support/fixtures/vcr_cassettes/xwiki/canonical_page_info.yml new file mode 100644 index 00000000000..85549582438 --- /dev/null +++ b/spec/support/fixtures/vcr_cassettes/xwiki/canonical_page_info.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: put + uri: https://xwiki.local/rest/openproject/documents?docRef=xwiki:Main.WebHome + body: + encoding: UTF-8 + string: "{}" + headers: + User-Agent: + - OpenProject 17.6.0 HTTPX Client + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Content-Type: + - application/json + Content-Length: + - '2' + Authorization: + - Bearer + response: + status: + code: 202 + message: Accepted + headers: + Content-Language: + - en + Content-Script-Type: + - text/javascript + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 05 Jun 2026 06:30:56 GMT + Set-Cookie: + - JSESSIONID=E1711FFA64B3AFE5CBB3FA5881A7138D; Path=/; HttpOnly + Xwiki-Form-Token: + - ON8xsHlEpixyujzpUPNupg + Xwiki-User: + - xwiki:XWiki.admin + Xwiki-Version: + - 18.3.0 + body: + encoding: UTF-8 + string: '{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main","rel":"http://www.xwiki.org/rel/space","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/children","rel":"http://www.xwiki.org/rel/children","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/objects","rel":"http://www.xwiki.org/rel/objects","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/syntaxes","rel":"http://www.xwiki.org/rel/syntaxes","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/openproject/documents","rel":"self","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/classes/Main.WebHome","rel":"http://www.xwiki.org/rel/class","type":null,"hrefLang":null}],"id":"484f4","fullName":"Main.WebHome","wiki":"xwiki","space":"Main","name":"WebHome","title":"Home","rawTitle":"Home","parent":"","parentId":"","version":"1.1","author":"XWiki.admin","authorName":null,"xwikiRelativeUrl":"https://xwiki.local/bin/view/Main/","xwikiAbsoluteUrl":"https://xwiki.local/bin/view/Main/","translations":{"links":[],"translations":[{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"en"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/pt_BR","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/pt_BR/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"pt_BR"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/ru","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/ru/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"ru"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/fr","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/fr/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"fr"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/no","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/no/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"no"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/da","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/da/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"da"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/de","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/de/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"de"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/en_GB","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/en_GB/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"en_GB"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/es","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/es/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"es"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/ca","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/ca/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"ca"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/ko","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/ko/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"ko"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/ja","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/ja/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"ja"},{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/uk","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Main/pages/WebHome/translations/uk/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null}],"language":"uk"}],"default":"en"},"syntax":"xwiki/2.1","language":"","majorVersion":1,"minorVersion":1,"hidden":false,"enforceRequiredRights":false,"created":1773821175000,"creator":"XWiki.admin","creatorName":null,"modified":1773821175000,"modifier":"XWiki.admin","modifierName":null,"originalMetadataAuthor":"xwiki:XWiki.admin","originalMetadataAuthorName":null,"comment":"Created + URL Shortener.","content":"== Welcome to your wiki ==\n\nXWiki is the best + tool to organize your knowledge. A //wiki// is organized in a hierarchy of + //pages//. You can create multiple wikis, each with its own set of pages.\n\nXWiki + can be used as a knowledge base (support, documentation, sales, etc.), for + collaborative workspaces or even as a complete intranet.\n\n== The basics + ==\n\nTo make the most out of your wiki, log-in and:\n\nUse the {{displayIcon + name=\"pencil\"/}} button above to //edit// this page and start customizing + your wiki to your needs.\n\nUse the {{displayIcon name=\"add\"/}} button above + to //add// more pages to your wiki and create the //hierarchy// that best + organizes your content.\n\nUse the {{displayIcon name=\"home\"/}} breadcrumbs + located above the title to //navigate// inside your pages. It''s easy to get + lost in a big wiki without them.\n\nYou can also use the [[Sandbox>>Sandbox.WebHome]] + for more demo content and generally a place to experiment with your wiki''s + features. \n\n {{box}}Learn more on how to use XWiki with the [[Getting Started + Guide>>https://www.xwiki.org/xwiki/bin/view/Documentation/UserGuide/GettingStarted/WebHome]].{{/box}}\n\n(% + class=\"row\" %)\n(((\n(% class=\"col-xs-12 col-sm-6\" %)\n(((\n== Extend + your wiki ==\n\nTo extend the power and functionalities of your wiki with + the features that //you// need, head over to the [[Extension Manager>>XWiki.XWikiPreferences||queryString=\"editor=globaladmin§ion=XWiki.Extensions\"]] + where you can search for and install extensions.\n\nTo browse through the + 900+ community contributed extensions available for XWiki, head over to the + [[Extensions Repository>>https://extensions.xwiki.org]].\n)))\n\n(% class=\"col-xs-12 + col-sm-6\" %)\n(((\n== Create your application ==\n\nGo beyond the available + extensions and define the //structure// of your data based on //your// needs, + creating //your// own applications with [[App Within Minutes>>AppWithinMinutes]] + (AWM). \n\nAWM will take care of making it easy for you and your users to + create and manage the data.\n)))\n)))","clazz":null,"objects":null,"attachments":null,"hierarchy":{"items":[{"label":"xwiki","name":"xwiki","type":"wiki","url":"https://xwiki.local/bin/view/Main/"},{"label":"Main","name":"Main","type":"space","url":"https://xwiki.local/bin/view/Main/"},{"label":"WebHome","name":"WebHome","type":"document","url":"https://xwiki.local/bin/view/Main/"}]},"rights":[],"renderedContent":null}' + recorded_at: Fri, 05 Jun 2026 06:30:56 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/support/fixtures/vcr_cassettes/xwiki/query_exact_match.yml b/spec/support/fixtures/vcr_cassettes/xwiki/query_exact_match.yml index 18637472313..8791bef049a 100644 --- a/spec/support/fixtures/vcr_cassettes/xwiki/query_exact_match.yml +++ b/spec/support/fixtures/vcr_cassettes/xwiki/query_exact_match.yml @@ -27,29 +27,29 @@ http_interactions: Content-Type: - application/json;charset=UTF-8 Date: - - Tue, 02 Jun 2026 08:17:22 GMT + - Fri, 05 Jun 2026 06:39:19 GMT Set-Cookie: - - JSESSIONID=A90934AE8F68AA76D0E0970863A314F8; Path=/; HttpOnly + - JSESSIONID=EF2A09F6E0681B4B0B3E44D639CB89D9; Path=/; HttpOnly Xwiki-Form-Token: - - 2XZUUfWArYhRgLYx4nHjhQ + - ON8xsHlEpixyujzpUPNupg Xwiki-User: - xwiki:XWiki.admin Xwiki-Version: - 18.3.0 Content-Length: - - '790' + - '789' body: encoding: UTF-8 string: '{"links":[],"searchResults":[{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null}],"type":"page","id":"xwiki:Test Page.WebHome","pageFullName":"Test Page.WebHome","title":"Test Page for RSpec","wiki":"xwiki","space":"Test - Page","pageName":"WebHome","modified":1780386902000,"author":"xwiki:XWiki.admin","authorName":null,"version":"4.1","language":null,"className":null,"objectNumber":null,"filename":null,"score":30.283539,"object":null,"hierarchy":null}],"template":"https://xwiki.local/rest/?q={solrquery}(&number={number})(&start={start})(&orderField={fieldname}(&order={asc|desc}))(&distinct=1)(&prettyNames={false|true})(&wikis={wikis})(&className={classname})"}' - recorded_at: Tue, 02 Jun 2026 08:17:22 GMT + Page","pageName":"WebHome","modified":1780386902000,"author":"xwiki:XWiki.admin","authorName":null,"version":"4.1","language":null,"className":null,"objectNumber":null,"filename":null,"score":32.03692,"object":null,"hierarchy":null}],"template":"https://xwiki.local/rest/?q={solrquery}(&number={number})(&start={start})(&orderField={fieldname}(&order={asc|desc}))(&distinct=1)(&prettyNames={false|true})(&wikis={wikis})(&className={classname})"}' + recorded_at: Fri, 05 Jun 2026 06:39:19 GMT - request: - method: get - uri: https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome + method: put + uri: https://xwiki.local/rest/openproject/documents?docRef=xwiki:Test%20Page.WebHome body: - encoding: US-ASCII - string: '' + encoding: UTF-8 + string: "{}" headers: User-Agent: - OpenProject 17.6.0 HTTPX Client @@ -57,12 +57,16 @@ http_interactions: - application/json Accept-Encoding: - gzip, deflate + Content-Type: + - application/json + Content-Length: + - '2' Authorization: - Bearer response: status: - code: 200 - message: OK + code: 202 + message: Accepted headers: Content-Language: - en @@ -71,24 +75,24 @@ http_interactions: Content-Type: - application/json;charset=UTF-8 Date: - - Tue, 02 Jun 2026 08:17:22 GMT + - Fri, 05 Jun 2026 06:39:19 GMT Set-Cookie: - - JSESSIONID=6D38F35499AE562F0EEE91795F9951CF; Path=/; HttpOnly + - JSESSIONID=CD75D58F42FA2FF29480F0AA679975CB; Path=/; HttpOnly Xwiki-Form-Token: - - 2XZUUfWArYhRgLYx4nHjhQ + - ON8xsHlEpixyujzpUPNupg Xwiki-User: - xwiki:XWiki.admin Xwiki-Version: - 18.3.0 Content-Length: - - '2224' + - '2372' body: encoding: UTF-8 - string: '{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page","rel":"http://www.xwiki.org/rel/space","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome","rel":"http://www.xwiki.org/rel/parent","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/syntaxes","rel":"http://www.xwiki.org/rel/syntaxes","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome","rel":"self","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/classes/Test%20Page.WebHome","rel":"http://www.xwiki.org/rel/class","type":null,"hrefLang":null}],"id":"xwiki:Test - Page.WebHome","fullName":"Test Page.WebHome","wiki":"xwiki","space":"Test - Page","name":"WebHome","title":"Test Page for RSpec","rawTitle":"Test Page - for RSpec","parent":"Main.WebHome","parentId":"xwiki:Main.WebHome","version":"4.1","author":"XWiki.admin","authorName":null,"xwikiRelativeUrl":"https://xwiki.local/bin/view/Test%20Page/","xwikiAbsoluteUrl":"https://xwiki.local/bin/view/Test%20Page/","translations":{"links":[],"translations":[],"default":"en"},"syntax":"xwiki/2.1","language":"","majorVersion":4,"minorVersion":1,"hidden":false,"enforceRequiredRights":false,"created":1780383689000,"creator":"XWiki.admin","creatorName":null,"modified":1780386902000,"modifier":"XWiki.admin","modifierName":null,"originalMetadataAuthor":"xwiki:XWiki.admin","originalMetadataAuthorName":null,"comment":"","content":"This - is a test page that I created with my own hands.","clazz":null,"objects":null,"attachments":null,"hierarchy":{"items":[{"label":"xwiki","name":"xwiki","type":"wiki","url":"https://xwiki.local/bin/view/Main/"},{"label":"Test + string: '{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page","rel":"http://www.xwiki.org/rel/space","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome","rel":"http://www.xwiki.org/rel/parent","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome/objects","rel":"http://www.xwiki.org/rel/objects","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/syntaxes","rel":"http://www.xwiki.org/rel/syntaxes","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/openproject/documents","rel":"self","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/classes/Test%20Page.WebHome","rel":"http://www.xwiki.org/rel/class","type":null,"hrefLang":null}],"id":"d70f1","fullName":"Test + Page.WebHome","wiki":"xwiki","space":"Test Page","name":"WebHome","title":"Test + Page for RSpec","rawTitle":"Test Page for RSpec","parent":"Main.WebHome","parentId":"xwiki:Main.WebHome","version":"4.1","author":"XWiki.admin","authorName":null,"xwikiRelativeUrl":"https://xwiki.local/bin/view/Test%20Page/","xwikiAbsoluteUrl":"https://xwiki.local/bin/view/Test%20Page/","translations":{"links":[],"translations":[],"default":"en"},"syntax":"xwiki/2.1","language":"","majorVersion":4,"minorVersion":1,"hidden":false,"enforceRequiredRights":false,"created":1780383689000,"creator":"XWiki.admin","creatorName":null,"modified":1780386902000,"modifier":"XWiki.admin","modifierName":null,"originalMetadataAuthor":"xwiki:XWiki.admin","originalMetadataAuthorName":null,"comment":"Created + URL Shortener.","content":"This is a test page that I created with my own + hands.","clazz":null,"objects":null,"attachments":null,"hierarchy":{"items":[{"label":"xwiki","name":"xwiki","type":"wiki","url":"https://xwiki.local/bin/view/Main/"},{"label":"Test Page","name":"Test Page","type":"space","url":"https://xwiki.local/bin/view/Test%20Page/"},{"label":"WebHome","name":"WebHome","type":"document","url":"https://xwiki.local/bin/view/Test%20Page/"}]},"rights":[],"renderedContent":null}' - recorded_at: Tue, 02 Jun 2026 08:17:22 GMT + recorded_at: Fri, 05 Jun 2026 06:39:19 GMT recorded_with: VCR 6.4.0 diff --git a/spec/support/fixtures/vcr_cassettes/xwiki/query_no_match.yml b/spec/support/fixtures/vcr_cassettes/xwiki/query_no_match.yml index 21d38b10029..c1158559d9d 100644 --- a/spec/support/fixtures/vcr_cassettes/xwiki/query_no_match.yml +++ b/spec/support/fixtures/vcr_cassettes/xwiki/query_no_match.yml @@ -27,11 +27,11 @@ http_interactions: Content-Type: - application/json;charset=UTF-8 Date: - - Tue, 02 Jun 2026 08:17:23 GMT + - Fri, 05 Jun 2026 06:39:20 GMT Set-Cookie: - - JSESSIONID=9D417B4F0A9D4C117C903A54394DAD6C; Path=/; HttpOnly + - JSESSIONID=D1B7C7CFA5520362BCB9E2DB63EFCE00; Path=/; HttpOnly Xwiki-Form-Token: - - 2XZUUfWArYhRgLYx4nHjhQ + - ON8xsHlEpixyujzpUPNupg Xwiki-User: - xwiki:XWiki.admin Xwiki-Version: @@ -41,5 +41,5 @@ http_interactions: body: encoding: UTF-8 string: '{"links":[],"searchResults":[],"template":"https://xwiki.local/rest/?q={solrquery}(&number={number})(&start={start})(&orderField={fieldname}(&order={asc|desc}))(&distinct=1)(&prettyNames={false|true})(&wikis={wikis})(&className={classname})"}' - recorded_at: Tue, 02 Jun 2026 08:17:23 GMT + recorded_at: Fri, 05 Jun 2026 06:39:20 GMT recorded_with: VCR 6.4.0 diff --git a/spec/support/fixtures/vcr_cassettes/xwiki/query_partial_match.yml b/spec/support/fixtures/vcr_cassettes/xwiki/query_partial_match.yml index 86708b6d8f9..a78c9afd3d3 100644 --- a/spec/support/fixtures/vcr_cassettes/xwiki/query_partial_match.yml +++ b/spec/support/fixtures/vcr_cassettes/xwiki/query_partial_match.yml @@ -27,11 +27,11 @@ http_interactions: Content-Type: - application/json;charset=UTF-8 Date: - - Tue, 02 Jun 2026 08:17:22 GMT + - Fri, 05 Jun 2026 06:39:19 GMT Set-Cookie: - - JSESSIONID=4570EBB986F92482503F494102024098; Path=/; HttpOnly + - JSESSIONID=689FB3235B4E5A8C46683A1331BD9EB2; Path=/; HttpOnly Xwiki-Form-Token: - - 2XZUUfWArYhRgLYx4nHjhQ + - ON8xsHlEpixyujzpUPNupg Xwiki-User: - xwiki:XWiki.admin Xwiki-Version: @@ -42,14 +42,14 @@ http_interactions: encoding: UTF-8 string: '{"links":[],"searchResults":[{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null}],"type":"page","id":"xwiki:Test Page.WebHome","pageFullName":"Test Page.WebHome","title":"Test Page for RSpec","wiki":"xwiki","space":"Test - Page","pageName":"WebHome","modified":1780386902000,"author":"xwiki:XWiki.admin","authorName":null,"version":"4.1","language":null,"className":null,"objectNumber":null,"filename":null,"score":14.608791,"object":null,"hierarchy":null}],"template":"https://xwiki.local/rest/?q={solrquery}(&number={number})(&start={start})(&orderField={fieldname}(&order={asc|desc}))(&distinct=1)(&prettyNames={false|true})(&wikis={wikis})(&className={classname})"}' - recorded_at: Tue, 02 Jun 2026 08:17:22 GMT + Page","pageName":"WebHome","modified":1780386902000,"author":"xwiki:XWiki.admin","authorName":null,"version":"4.1","language":null,"className":null,"objectNumber":null,"filename":null,"score":14.085169,"object":null,"hierarchy":null}],"template":"https://xwiki.local/rest/?q={solrquery}(&number={number})(&start={start})(&orderField={fieldname}(&order={asc|desc}))(&distinct=1)(&prettyNames={false|true})(&wikis={wikis})(&className={classname})"}' + recorded_at: Fri, 05 Jun 2026 06:39:19 GMT - request: - method: get - uri: https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome + method: put + uri: https://xwiki.local/rest/openproject/documents?docRef=xwiki:Test%20Page.WebHome body: - encoding: US-ASCII - string: '' + encoding: UTF-8 + string: "{}" headers: User-Agent: - OpenProject 17.6.0 HTTPX Client @@ -57,12 +57,16 @@ http_interactions: - application/json Accept-Encoding: - gzip, deflate + Content-Type: + - application/json + Content-Length: + - '2' Authorization: - Bearer response: status: - code: 200 - message: OK + code: 202 + message: Accepted headers: Content-Language: - en @@ -71,24 +75,24 @@ http_interactions: Content-Type: - application/json;charset=UTF-8 Date: - - Tue, 02 Jun 2026 08:17:22 GMT + - Fri, 05 Jun 2026 06:39:19 GMT Set-Cookie: - - JSESSIONID=36828A84C85C858E144AA581B1A1F138; Path=/; HttpOnly + - JSESSIONID=41BB94B7774F3AAF5872EC50FF78C37B; Path=/; HttpOnly Xwiki-Form-Token: - - 2XZUUfWArYhRgLYx4nHjhQ + - ON8xsHlEpixyujzpUPNupg Xwiki-User: - xwiki:XWiki.admin Xwiki-Version: - 18.3.0 Content-Length: - - '2224' + - '2372' body: encoding: UTF-8 - string: '{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page","rel":"http://www.xwiki.org/rel/space","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome","rel":"http://www.xwiki.org/rel/parent","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/syntaxes","rel":"http://www.xwiki.org/rel/syntaxes","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome","rel":"self","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/classes/Test%20Page.WebHome","rel":"http://www.xwiki.org/rel/class","type":null,"hrefLang":null}],"id":"xwiki:Test - Page.WebHome","fullName":"Test Page.WebHome","wiki":"xwiki","space":"Test - Page","name":"WebHome","title":"Test Page for RSpec","rawTitle":"Test Page - for RSpec","parent":"Main.WebHome","parentId":"xwiki:Main.WebHome","version":"4.1","author":"XWiki.admin","authorName":null,"xwikiRelativeUrl":"https://xwiki.local/bin/view/Test%20Page/","xwikiAbsoluteUrl":"https://xwiki.local/bin/view/Test%20Page/","translations":{"links":[],"translations":[],"default":"en"},"syntax":"xwiki/2.1","language":"","majorVersion":4,"minorVersion":1,"hidden":false,"enforceRequiredRights":false,"created":1780383689000,"creator":"XWiki.admin","creatorName":null,"modified":1780386902000,"modifier":"XWiki.admin","modifierName":null,"originalMetadataAuthor":"xwiki:XWiki.admin","originalMetadataAuthorName":null,"comment":"","content":"This - is a test page that I created with my own hands.","clazz":null,"objects":null,"attachments":null,"hierarchy":{"items":[{"label":"xwiki","name":"xwiki","type":"wiki","url":"https://xwiki.local/bin/view/Main/"},{"label":"Test + string: '{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page","rel":"http://www.xwiki.org/rel/space","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome","rel":"http://www.xwiki.org/rel/parent","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome/objects","rel":"http://www.xwiki.org/rel/objects","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/syntaxes","rel":"http://www.xwiki.org/rel/syntaxes","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/openproject/documents","rel":"self","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/classes/Test%20Page.WebHome","rel":"http://www.xwiki.org/rel/class","type":null,"hrefLang":null}],"id":"d70f1","fullName":"Test + Page.WebHome","wiki":"xwiki","space":"Test Page","name":"WebHome","title":"Test + Page for RSpec","rawTitle":"Test Page for RSpec","parent":"Main.WebHome","parentId":"xwiki:Main.WebHome","version":"4.1","author":"XWiki.admin","authorName":null,"xwikiRelativeUrl":"https://xwiki.local/bin/view/Test%20Page/","xwikiAbsoluteUrl":"https://xwiki.local/bin/view/Test%20Page/","translations":{"links":[],"translations":[],"default":"en"},"syntax":"xwiki/2.1","language":"","majorVersion":4,"minorVersion":1,"hidden":false,"enforceRequiredRights":false,"created":1780383689000,"creator":"XWiki.admin","creatorName":null,"modified":1780386902000,"modifier":"XWiki.admin","modifierName":null,"originalMetadataAuthor":"xwiki:XWiki.admin","originalMetadataAuthorName":null,"comment":"Created + URL Shortener.","content":"This is a test page that I created with my own + hands.","clazz":null,"objects":null,"attachments":null,"hierarchy":{"items":[{"label":"xwiki","name":"xwiki","type":"wiki","url":"https://xwiki.local/bin/view/Main/"},{"label":"Test Page","name":"Test Page","type":"space","url":"https://xwiki.local/bin/view/Test%20Page/"},{"label":"WebHome","name":"WebHome","type":"document","url":"https://xwiki.local/bin/view/Test%20Page/"}]},"rights":[],"renderedContent":null}' - recorded_at: Tue, 02 Jun 2026 08:17:22 GMT + recorded_at: Fri, 05 Jun 2026 06:39:19 GMT recorded_with: VCR 6.4.0 diff --git a/spec/support/fixtures/vcr_cassettes/xwiki/query_quoted_match.yml b/spec/support/fixtures/vcr_cassettes/xwiki/query_quoted_match.yml index bc3063416a4..b1238e1edde 100644 --- a/spec/support/fixtures/vcr_cassettes/xwiki/query_quoted_match.yml +++ b/spec/support/fixtures/vcr_cassettes/xwiki/query_quoted_match.yml @@ -27,11 +27,11 @@ http_interactions: Content-Type: - application/json;charset=UTF-8 Date: - - Tue, 02 Jun 2026 08:17:22 GMT + - Fri, 05 Jun 2026 06:39:20 GMT Set-Cookie: - - JSESSIONID=C28E0999BF663CA702569EE1C5C696E2; Path=/; HttpOnly + - JSESSIONID=A1B5B0EFF5FD5E7B4F6E4CB0D185B065; Path=/; HttpOnly Xwiki-Form-Token: - - 2XZUUfWArYhRgLYx4nHjhQ + - ON8xsHlEpixyujzpUPNupg Xwiki-User: - xwiki:XWiki.admin Xwiki-Version: @@ -42,14 +42,14 @@ http_interactions: encoding: UTF-8 string: '{"links":[],"searchResults":[{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null}],"type":"page","id":"xwiki:\"Quoted\" pages can be tricky.WebHome","pageFullName":"\"Quoted\" pages can be tricky.WebHome","title":"\"Quoted\" - pages can be tricky","wiki":"xwiki","space":"\"Quoted\" pages can be tricky","pageName":"WebHome","modified":1780387197000,"author":"xwiki:XWiki.admin","authorName":null,"version":"1.1","language":null,"className":null,"objectNumber":null,"filename":null,"score":42.605682,"object":null,"hierarchy":null}],"template":"https://xwiki.local/rest/?q={solrquery}(&number={number})(&start={start})(&orderField={fieldname}(&order={asc|desc}))(&distinct=1)(&prettyNames={false|true})(&wikis={wikis})(&className={classname})"}' - recorded_at: Tue, 02 Jun 2026 08:17:22 GMT + pages can be tricky","wiki":"xwiki","space":"\"Quoted\" pages can be tricky","pageName":"WebHome","modified":1780387197000,"author":"xwiki:XWiki.admin","authorName":null,"version":"1.1","language":null,"className":null,"objectNumber":null,"filename":null,"score":41.692856,"object":null,"hierarchy":null}],"template":"https://xwiki.local/rest/?q={solrquery}(&number={number})(&start={start})(&orderField={fieldname}(&order={asc|desc}))(&distinct=1)(&prettyNames={false|true})(&wikis={wikis})(&className={classname})"}' + recorded_at: Fri, 05 Jun 2026 06:39:20 GMT - request: - method: get - uri: https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome + method: put + uri: https://xwiki.local/rest/openproject/documents?docRef=xwiki:%22Quoted%22%20pages%20can%20be%20tricky.WebHome body: - encoding: US-ASCII - string: '' + encoding: UTF-8 + string: "{}" headers: User-Agent: - OpenProject 17.6.0 HTTPX Client @@ -57,12 +57,16 @@ http_interactions: - application/json Accept-Encoding: - gzip, deflate + Content-Type: + - application/json + Content-Length: + - '2' Authorization: - Bearer response: status: - code: 200 - message: OK + code: 202 + message: Accepted headers: Content-Language: - en @@ -71,24 +75,25 @@ http_interactions: Content-Type: - application/json;charset=UTF-8 Date: - - Tue, 02 Jun 2026 08:17:22 GMT + - Fri, 05 Jun 2026 06:39:20 GMT Set-Cookie: - - JSESSIONID=EC142E0079C91196143B051273FDA2FD; Path=/; HttpOnly + - JSESSIONID=9F577E6A3744F626EDDD3668B5F9EE9D; Path=/; HttpOnly Xwiki-Form-Token: - - 2XZUUfWArYhRgLYx4nHjhQ + - ON8xsHlEpixyujzpUPNupg Xwiki-User: - xwiki:XWiki.admin Xwiki-Version: - 18.3.0 Content-Length: - - '2642' + - '2769' body: encoding: UTF-8 - string: '{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky","rel":"http://www.xwiki.org/rel/space","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome","rel":"http://www.xwiki.org/rel/parent","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/syntaxes","rel":"http://www.xwiki.org/rel/syntaxes","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome","rel":"self","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/classes/%22Quoted%22%20pages%20can%20be%20tricky.WebHome","rel":"http://www.xwiki.org/rel/class","type":null,"hrefLang":null}],"id":"xwiki:\"Quoted\" - pages can be tricky.WebHome","fullName":"\"Quoted\" pages can be tricky.WebHome","wiki":"xwiki","space":"\"Quoted\" - pages can be tricky","name":"WebHome","title":"\"Quoted\" pages can be tricky","rawTitle":"\"Quoted\" - pages can be tricky","parent":"Main.WebHome","parentId":"xwiki:Main.WebHome","version":"1.1","author":"XWiki.admin","authorName":null,"xwikiRelativeUrl":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/","xwikiAbsoluteUrl":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/","translations":{"links":[],"translations":[],"default":"en"},"syntax":"xwiki/2.1","language":"","majorVersion":1,"minorVersion":1,"hidden":false,"enforceRequiredRights":false,"created":1780387197000,"creator":"XWiki.admin","creatorName":null,"modified":1780387197000,"modifier":"XWiki.admin","modifierName":null,"originalMetadataAuthor":"xwiki:XWiki.admin","originalMetadataAuthorName":null,"comment":"","content":"When - the page title contains quotes, it''s harder to exactly match their page title.","clazz":null,"objects":null,"attachments":null,"hierarchy":{"items":[{"label":"xwiki","name":"xwiki","type":"wiki","url":"https://xwiki.local/bin/view/Main/"},{"label":"\"Quoted\" + string: '{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky","rel":"http://www.xwiki.org/rel/space","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome","rel":"http://www.xwiki.org/rel/parent","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome/objects","rel":"http://www.xwiki.org/rel/objects","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/syntaxes","rel":"http://www.xwiki.org/rel/syntaxes","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/openproject/documents","rel":"self","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/classes/%22Quoted%22%20pages%20can%20be%20tricky.WebHome","rel":"http://www.xwiki.org/rel/class","type":null,"hrefLang":null}],"id":"1a013","fullName":"\"Quoted\" + pages can be tricky.WebHome","wiki":"xwiki","space":"\"Quoted\" pages can + be tricky","name":"WebHome","title":"\"Quoted\" pages can be tricky","rawTitle":"\"Quoted\" + pages can be tricky","parent":"Main.WebHome","parentId":"xwiki:Main.WebHome","version":"1.1","author":"XWiki.admin","authorName":null,"xwikiRelativeUrl":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/","xwikiAbsoluteUrl":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/","translations":{"links":[],"translations":[],"default":"en"},"syntax":"xwiki/2.1","language":"","majorVersion":1,"minorVersion":1,"hidden":false,"enforceRequiredRights":false,"created":1780387197000,"creator":"XWiki.admin","creatorName":null,"modified":1780387197000,"modifier":"XWiki.admin","modifierName":null,"originalMetadataAuthor":"xwiki:XWiki.admin","originalMetadataAuthorName":null,"comment":"Created + URL Shortener.","content":"When the page title contains quotes, it''s harder + to exactly match their page title.","clazz":null,"objects":null,"attachments":null,"hierarchy":{"items":[{"label":"xwiki","name":"xwiki","type":"wiki","url":"https://xwiki.local/bin/view/Main/"},{"label":"\"Quoted\" pages can be tricky","name":"\"Quoted\" pages can be tricky","type":"space","url":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/"},{"label":"WebHome","name":"WebHome","type":"document","url":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/"}]},"rights":[],"renderedContent":null}' - recorded_at: Tue, 02 Jun 2026 08:17:22 GMT + recorded_at: Fri, 05 Jun 2026 06:39:20 GMT recorded_with: VCR 6.4.0 diff --git a/spec/support/fixtures/vcr_cassettes/xwiki/query_unquoted_match.yml b/spec/support/fixtures/vcr_cassettes/xwiki/query_unquoted_match.yml index db53a2fc824..c46c68fc370 100644 --- a/spec/support/fixtures/vcr_cassettes/xwiki/query_unquoted_match.yml +++ b/spec/support/fixtures/vcr_cassettes/xwiki/query_unquoted_match.yml @@ -27,11 +27,11 @@ http_interactions: Content-Type: - application/json;charset=UTF-8 Date: - - Tue, 02 Jun 2026 08:17:23 GMT + - Fri, 05 Jun 2026 06:39:20 GMT Set-Cookie: - - JSESSIONID=10D557C0A4C7D3C599B80C2FA01F5FF5; Path=/; HttpOnly + - JSESSIONID=B47446E1E06E6D2A73FC512CEAB8083D; Path=/; HttpOnly Xwiki-Form-Token: - - 2XZUUfWArYhRgLYx4nHjhQ + - ON8xsHlEpixyujzpUPNupg Xwiki-User: - xwiki:XWiki.admin Xwiki-Version: @@ -42,14 +42,14 @@ http_interactions: encoding: UTF-8 string: '{"links":[],"searchResults":[{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome","rel":"http://www.xwiki.org/rel/page","type":null,"hrefLang":null}],"type":"page","id":"xwiki:\"Quoted\" pages can be tricky.WebHome","pageFullName":"\"Quoted\" pages can be tricky.WebHome","title":"\"Quoted\" - pages can be tricky","wiki":"xwiki","space":"\"Quoted\" pages can be tricky","pageName":"WebHome","modified":1780387197000,"author":"xwiki:XWiki.admin","authorName":null,"version":"1.1","language":null,"className":null,"objectNumber":null,"filename":null,"score":42.605682,"object":null,"hierarchy":null}],"template":"https://xwiki.local/rest/?q={solrquery}(&number={number})(&start={start})(&orderField={fieldname}(&order={asc|desc}))(&distinct=1)(&prettyNames={false|true})(&wikis={wikis})(&className={classname})"}' - recorded_at: Tue, 02 Jun 2026 08:17:23 GMT + pages can be tricky","wiki":"xwiki","space":"\"Quoted\" pages can be tricky","pageName":"WebHome","modified":1780387197000,"author":"xwiki:XWiki.admin","authorName":null,"version":"1.1","language":null,"className":null,"objectNumber":null,"filename":null,"score":41.692856,"object":null,"hierarchy":null}],"template":"https://xwiki.local/rest/?q={solrquery}(&number={number})(&start={start})(&orderField={fieldname}(&order={asc|desc}))(&distinct=1)(&prettyNames={false|true})(&wikis={wikis})(&className={classname})"}' + recorded_at: Fri, 05 Jun 2026 06:39:20 GMT - request: - method: get - uri: https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome + method: put + uri: https://xwiki.local/rest/openproject/documents?docRef=xwiki:%22Quoted%22%20pages%20can%20be%20tricky.WebHome body: - encoding: US-ASCII - string: '' + encoding: UTF-8 + string: "{}" headers: User-Agent: - OpenProject 17.6.0 HTTPX Client @@ -57,12 +57,16 @@ http_interactions: - application/json Accept-Encoding: - gzip, deflate + Content-Type: + - application/json + Content-Length: + - '2' Authorization: - Bearer response: status: - code: 200 - message: OK + code: 202 + message: Accepted headers: Content-Language: - en @@ -71,24 +75,25 @@ http_interactions: Content-Type: - application/json;charset=UTF-8 Date: - - Tue, 02 Jun 2026 08:17:23 GMT + - Fri, 05 Jun 2026 06:39:20 GMT Set-Cookie: - - JSESSIONID=85112D2673FA7F3B99AEC6BF0A182372; Path=/; HttpOnly + - JSESSIONID=2B1297828F8D8887496627F7356421D5; Path=/; HttpOnly Xwiki-Form-Token: - - 2XZUUfWArYhRgLYx4nHjhQ + - ON8xsHlEpixyujzpUPNupg Xwiki-User: - xwiki:XWiki.admin Xwiki-Version: - 18.3.0 Content-Length: - - '2642' + - '2769' body: encoding: UTF-8 - string: '{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky","rel":"http://www.xwiki.org/rel/space","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome","rel":"http://www.xwiki.org/rel/parent","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/syntaxes","rel":"http://www.xwiki.org/rel/syntaxes","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome","rel":"self","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/classes/%22Quoted%22%20pages%20can%20be%20tricky.WebHome","rel":"http://www.xwiki.org/rel/class","type":null,"hrefLang":null}],"id":"xwiki:\"Quoted\" - pages can be tricky.WebHome","fullName":"\"Quoted\" pages can be tricky.WebHome","wiki":"xwiki","space":"\"Quoted\" - pages can be tricky","name":"WebHome","title":"\"Quoted\" pages can be tricky","rawTitle":"\"Quoted\" - pages can be tricky","parent":"Main.WebHome","parentId":"xwiki:Main.WebHome","version":"1.1","author":"XWiki.admin","authorName":null,"xwikiRelativeUrl":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/","xwikiAbsoluteUrl":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/","translations":{"links":[],"translations":[],"default":"en"},"syntax":"xwiki/2.1","language":"","majorVersion":1,"minorVersion":1,"hidden":false,"enforceRequiredRights":false,"created":1780387197000,"creator":"XWiki.admin","creatorName":null,"modified":1780387197000,"modifier":"XWiki.admin","modifierName":null,"originalMetadataAuthor":"xwiki:XWiki.admin","originalMetadataAuthorName":null,"comment":"","content":"When - the page title contains quotes, it''s harder to exactly match their page title.","clazz":null,"objects":null,"attachments":null,"hierarchy":{"items":[{"label":"xwiki","name":"xwiki","type":"wiki","url":"https://xwiki.local/bin/view/Main/"},{"label":"\"Quoted\" + string: '{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky","rel":"http://www.xwiki.org/rel/space","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome","rel":"http://www.xwiki.org/rel/parent","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/%22Quoted%22%20pages%20can%20be%20tricky/pages/WebHome/objects","rel":"http://www.xwiki.org/rel/objects","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/syntaxes","rel":"http://www.xwiki.org/rel/syntaxes","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/openproject/documents","rel":"self","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/classes/%22Quoted%22%20pages%20can%20be%20tricky.WebHome","rel":"http://www.xwiki.org/rel/class","type":null,"hrefLang":null}],"id":"1a013","fullName":"\"Quoted\" + pages can be tricky.WebHome","wiki":"xwiki","space":"\"Quoted\" pages can + be tricky","name":"WebHome","title":"\"Quoted\" pages can be tricky","rawTitle":"\"Quoted\" + pages can be tricky","parent":"Main.WebHome","parentId":"xwiki:Main.WebHome","version":"1.1","author":"XWiki.admin","authorName":null,"xwikiRelativeUrl":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/","xwikiAbsoluteUrl":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/","translations":{"links":[],"translations":[],"default":"en"},"syntax":"xwiki/2.1","language":"","majorVersion":1,"minorVersion":1,"hidden":false,"enforceRequiredRights":false,"created":1780387197000,"creator":"XWiki.admin","creatorName":null,"modified":1780387197000,"modifier":"XWiki.admin","modifierName":null,"originalMetadataAuthor":"xwiki:XWiki.admin","originalMetadataAuthorName":null,"comment":"Created + URL Shortener.","content":"When the page title contains quotes, it''s harder + to exactly match their page title.","clazz":null,"objects":null,"attachments":null,"hierarchy":{"items":[{"label":"xwiki","name":"xwiki","type":"wiki","url":"https://xwiki.local/bin/view/Main/"},{"label":"\"Quoted\" pages can be tricky","name":"\"Quoted\" pages can be tricky","type":"space","url":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/"},{"label":"WebHome","name":"WebHome","type":"document","url":"https://xwiki.local/bin/view/%22Quoted%22%20pages%20can%20be%20tricky/"}]},"rights":[],"renderedContent":null}' - recorded_at: Tue, 02 Jun 2026 08:17:23 GMT + recorded_at: Fri, 05 Jun 2026 06:39:20 GMT recorded_with: VCR 6.4.0 diff --git a/spec/support/fixtures/vcr_cassettes/xwiki/stable_page_info.yml b/spec/support/fixtures/vcr_cassettes/xwiki/stable_page_info.yml new file mode 100644 index 00000000000..0d7cd62208a --- /dev/null +++ b/spec/support/fixtures/vcr_cassettes/xwiki/stable_page_info.yml @@ -0,0 +1,50 @@ +--- +http_interactions: +- request: + method: get + uri: https://xwiki.local/rest/openproject/documents/d70f1 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - OpenProject 17.6.0 HTTPX Client + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer + response: + status: + code: 200 + message: OK + headers: + Content-Language: + - en + Content-Script-Type: + - text/javascript + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 05 Jun 2026 06:33:33 GMT + Set-Cookie: + - JSESSIONID=20FFAE29A62BFE6CEC0152BB7FC0ADB6; Path=/; HttpOnly + Xwiki-Form-Token: + - ON8xsHlEpixyujzpUPNupg + Xwiki-User: + - xwiki:XWiki.admin + Xwiki-Version: + - 18.3.0 + Content-Length: + - '2378' + body: + encoding: UTF-8 + string: '{"links":[{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page","rel":"http://www.xwiki.org/rel/space","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome","rel":"http://www.xwiki.org/rel/parent","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/spaces/Test%20Page/pages/WebHome/objects","rel":"http://www.xwiki.org/rel/objects","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/syntaxes","rel":"http://www.xwiki.org/rel/syntaxes","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/openproject/documents/d70f1","rel":"self","type":null,"hrefLang":null},{"href":"https://xwiki.local/rest/wikis/xwiki/classes/Test%20Page.WebHome","rel":"http://www.xwiki.org/rel/class","type":null,"hrefLang":null}],"id":"d70f1","fullName":"Test + Page.WebHome","wiki":"xwiki","space":"Test Page","name":"WebHome","title":"Test + Page for RSpec","rawTitle":"Test Page for RSpec","parent":"Main.WebHome","parentId":"xwiki:Main.WebHome","version":"4.1","author":"XWiki.admin","authorName":null,"xwikiRelativeUrl":"https://xwiki.local/bin/view/Test%20Page/","xwikiAbsoluteUrl":"https://xwiki.local/bin/view/Test%20Page/","translations":{"links":[],"translations":[],"default":"en"},"syntax":"xwiki/2.1","language":"","majorVersion":4,"minorVersion":1,"hidden":false,"enforceRequiredRights":false,"created":1780383689000,"creator":"XWiki.admin","creatorName":null,"modified":1780386902000,"modifier":"XWiki.admin","modifierName":null,"originalMetadataAuthor":"xwiki:XWiki.admin","originalMetadataAuthorName":null,"comment":"Created + URL Shortener.","content":"This is a test page that I created with my own + hands.","clazz":null,"objects":null,"attachments":null,"hierarchy":{"items":[{"label":"xwiki","name":"xwiki","type":"wiki","url":"https://xwiki.local/bin/view/Main/"},{"label":"Test + Page","name":"Test Page","type":"space","url":"https://xwiki.local/bin/view/Test%20Page/"},{"label":"WebHome","name":"WebHome","type":"document","url":"https://xwiki.local/bin/view/Test%20Page/"}]},"rights":[],"renderedContent":null}' + recorded_at: Fri, 05 Jun 2026 06:33:33 GMT +recorded_with: VCR 6.4.0