diff --git a/modules/storages/app/common/storages/peripherals/nextcloud_registry.rb b/modules/storages/app/common/storages/peripherals/nextcloud_registry.rb
index 88ec1b803187..93b35affbade 100644
--- a/modules/storages/app/common/storages/peripherals/nextcloud_registry.rb
+++ b/modules/storages/app/common/storages/peripherals/nextcloud_registry.rb
@@ -35,7 +35,6 @@ module Peripherals
register(:auth_check, StorageInteraction::Nextcloud::AuthCheckQuery)
register(:capabilities, StorageInteraction::Nextcloud::CapabilitiesQuery)
register(:download_link, StorageInteraction::Nextcloud::DownloadLinkQuery)
- register(:file_ids, StorageInteraction::Nextcloud::FileIdsQuery)
register(:file_info, StorageInteraction::Nextcloud::FileInfoQuery)
register(:files_info, StorageInteraction::Nextcloud::FilesInfoQuery)
register(:files, StorageInteraction::Nextcloud::FilesQuery)
diff --git a/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/copy_template_folder_command.rb b/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/copy_template_folder_command.rb
index 6b3aef7fdfba..c69ef2e6d210 100644
--- a/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/copy_template_folder_command.rb
+++ b/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/copy_template_folder_command.rb
@@ -46,15 +46,17 @@ def initialize(storage)
def call(auth_strategy:, source_path:, destination_path:)
with_tagged_logger do
- valid_input_result = validate_inputs(source_path, destination_path).on_failure { return _1 }
+ Authentication[auth_strategy].call(storage: @storage) do |http|
+ valid_input_result = validate_inputs(source_path, destination_path).on_failure { return _1 }
- remote_urls = build_origin_urls(**valid_input_result.result)
+ remote_urls = build_origin_urls(**valid_input_result.result)
- ensure_remote_folder_does_not_exist(auth_strategy, remote_urls[:destination_url]).on_failure { return _1 }
+ ensure_remote_folder_does_not_exist(http, remote_urls[:destination_url]).on_failure { return _1 }
- copy_folder(auth_strategy, **remote_urls).on_failure { return _1 }
+ copy_folder(http, **remote_urls).on_failure { return _1 }
- get_folder_id(valid_input_result.result[:destination_path])
+ get_folder_id(auth_strategy, valid_input_result.result[:destination_path])
+ end
end
end
@@ -76,9 +78,9 @@ def build_origin_urls(source_path:, destination_path:)
{ source_url:, destination_url: }
end
- def ensure_remote_folder_does_not_exist(auth_strategy, destination_url)
+ def ensure_remote_folder_does_not_exist(http, destination_url)
info "Checking if #{destination_url} does not already exists."
- response = Authentication[auth_strategy].call(storage: @storage) { |http| http.head(destination_url) }
+ response = http.head(destination_url)
case response
in { status: 200..299 }
@@ -98,13 +100,11 @@ def ensure_remote_folder_does_not_exist(auth_strategy, destination_url)
end
end
- def copy_folder(auth_strategy, source_url:, destination_url:)
+ def copy_folder(http, source_url:, destination_url:)
info "Copying #{source_url} to #{destination_url}"
- response = Authentication[auth_strategy].call(storage: @storage) do |http|
- http.request("COPY", source_url, headers: { "Destination" => destination_url, "Depth" => "infinity" })
- end
-
- handle_response(response)
+ handle_response http.request("COPY",
+ source_url,
+ headers: { "Destination" => destination_url, "Depth" => "infinity" })
end
# rubocop:disable Metrics/AbcSize
@@ -133,14 +133,14 @@ def handle_response(response)
errors: Util.storage_error(response:, code: :error, source:))
end
end
- # rubocop:enable Metrics/AbcSize
- def get_folder_id(destination_path)
- call = Registry
- .resolve("#{@storage.short_provider_type}.queries.file_ids")
- .call(storage: @storage, path: destination_path)
+ # rubocop:enable Metrics/AbcSize
- call.map { |result| @data.with(id: result[destination_path]["fileid"]) }
+ def get_folder_id(auth_strategy, destination_path)
+ Registry
+ .resolve("nextcloud.queries.file_path_to_id_map")
+ .call(storage: @storage, auth_strategy:, folder: ParentFolder.new(destination_path), depth: 0)
+ .map { |result| @data.with(id: result[destination_path].id) }
end
def source = self.class
diff --git a/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/file_ids_query.rb b/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/file_ids_query.rb
deleted file mode 100644
index 6b03b12b84e5..000000000000
--- a/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/file_ids_query.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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 Storages
- module Peripherals
- module StorageInteraction
- module Nextcloud
- class FileIdsQuery
- include TaggedLogging
- def self.call(storage:, path:)
- new(storage).call(path:)
- end
-
- def initialize(storage)
- @query = Internal::PropfindQueryLegacy.new(storage)
- end
-
- def call(path:)
- query_params = { depth: "1", path:, props: %w[oc:fileid] }
- with_tagged_logger do
- info "Requesting File Ids on path: #{path} and args: #{query_params.inspect}"
- @query.call(**query_params)
- end
- end
- end
- end
- end
- end
-end
diff --git a/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/file_path_to_id_map_query.rb b/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/file_path_to_id_map_query.rb
index 9ac13dd9f5cd..cb0a766c5077 100644
--- a/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/file_path_to_id_map_query.rb
+++ b/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/file_path_to_id_map_query.rb
@@ -33,8 +33,8 @@ module Peripherals
module StorageInteraction
module Nextcloud
class FilePathToIdMapQuery
- def self.call(storage:, auth_strategy:, folder:)
- new(storage).call(auth_strategy:, folder:)
+ def self.call(storage:, auth_strategy:, folder:, depth: Float::INFINITY)
+ new(storage).call(auth_strategy:, folder:, depth:)
end
def initialize(storage)
@@ -42,16 +42,15 @@ def initialize(storage)
@propfind_query = Internal::PropfindQuery.new(storage)
end
- def call(auth_strategy:, folder:)
+ def call(auth_strategy:, folder:, depth:)
origin_user_id = Util.origin_user_id(caller: self.class, storage: @storage, auth_strategy:)
- .on_failure do |result|
- return result
- end
+ .on_failure { return _1 }
+ .result
- Authentication[auth_strategy].call(storage: @storage, http_options:) do |http|
+ Authentication[auth_strategy].call(storage: @storage, http_options: headers(depth)) do |http|
# nc:acl-list is only required to avoid https://community.openproject.org/wp/49628. See comment #4.
@propfind_query.call(http:,
- username: origin_user_id.result,
+ username: origin_user_id,
path: folder.path,
props: %w[oc:fileid nc:acl-list])
.map do |obj|
@@ -62,8 +61,8 @@ def call(auth_strategy:, folder:)
private
- def http_options
- Util.webdav_request_with_depth("infinity")
+ def headers(depth)
+ Util.webdav_request_with_depth(depth.to_s.downcase)
end
end
end
diff --git a/modules/storages/app/common/storages/peripherals/storage_interaction/one_drive/file_path_to_id_map_query.rb b/modules/storages/app/common/storages/peripherals/storage_interaction/one_drive/file_path_to_id_map_query.rb
index a13c61ff1143..7e4e7aa8299d 100644
--- a/modules/storages/app/common/storages/peripherals/storage_interaction/one_drive/file_path_to_id_map_query.rb
+++ b/modules/storages/app/common/storages/peripherals/storage_interaction/one_drive/file_path_to_id_map_query.rb
@@ -36,8 +36,8 @@ class FilePathToIdMapQuery
CHILDREN_FIELDS = %w[id name file folder parentReference].freeze
FOLDER_FIELDS = %w[id name parentReference].freeze
- def self.call(storage:, auth_strategy:, folder:)
- new(storage).call(auth_strategy:, folder:)
+ def self.call(storage:, auth_strategy:, folder:, depth: Float::INFINITY)
+ new(storage).call(auth_strategy:, folder:, depth:)
end
def initialize(storage)
@@ -46,15 +46,18 @@ def initialize(storage)
@drive_item_query = Internal::DriveItemQuery.new(storage)
end
- def call(auth_strategy:, folder:)
+ # rubocop:disable Metrics/AbcSize
+ def call(auth_strategy:, folder:, depth:)
Authentication[auth_strategy].call(storage: @storage) do |http|
- fetch_result = fetch_folder(http, folder)
- return fetch_result if fetch_result.failure?
+ fetched_folder = fetch_folder(http, folder)
+ .on_failure { return _1 }
+ .result
- file_ids_dictionary = fetch_result.result
+ file_ids_dictionary = fetched_folder
queue = [folder]
+ level = 0
- while queue.any?
+ while queue.any? && level < depth
dir = queue.shift
visit = visit(http, dir)
@@ -63,12 +66,15 @@ def call(auth_strategy:, folder:)
entry, to_queue = visit.result.values_at(:entry, :to_queue)
file_ids_dictionary = file_ids_dictionary.merge(entry)
queue.concat(to_queue)
+ level += 1
end
ServiceResult.success(result: file_ids_dictionary)
end
end
+ # rubocop:enable Metrics/AbcSize
+
private
def visit(http, folder)
diff --git a/modules/storages/app/services/storages/nextcloud_managed_folder_sync_service.rb b/modules/storages/app/services/storages/nextcloud_managed_folder_sync_service.rb
index 0bd046524fde..617ab1aec04d 100644
--- a/modules/storages/app/services/storages/nextcloud_managed_folder_sync_service.rb
+++ b/modules/storages/app/services/storages/nextcloud_managed_folder_sync_service.rb
@@ -34,9 +34,14 @@ class NextcloudManagedFolderSyncService < BaseService
FILE_PERMISSIONS = OpenProject::Storages::Engine.external_file_permissions
- include Injector["nextcloud.commands.create_folder", "nextcloud.commands.rename_file", "nextcloud.commands.set_permissions",
- "nextcloud.queries.group_users", "nextcloud.queries.file_ids", "nextcloud.authentication.userless",
- "nextcloud.commands.add_user_to_group", "nextcloud.commands.remove_user_from_group"]
+ include Injector["nextcloud.commands.create_folder",
+ "nextcloud.commands.rename_file",
+ "nextcloud.commands.set_permissions",
+ "nextcloud.queries.group_users",
+ "nextcloud.queries.file_path_to_id_map",
+ "nextcloud.authentication.userless",
+ "nextcloud.commands.add_user_to_group",
+ "nextcloud.commands.remove_user_from_group"]
def self.i18n_key = "NextcloudSyncService"
@@ -76,7 +81,7 @@ def prepare_remote_folders
remote_folders = remote_root_folder_map(@storage.group_folder).on_failure { return _1 }.result
info "Found #{remote_folders.count} remote folders"
- ensure_root_folder_permissions(remote_folders["/#{@storage.group_folder}/"]["fileid"]).on_failure { return _1 }
+ ensure_root_folder_permissions(remote_folders["/#{@storage.group_folder}"].id).on_failure { return _1 }
ensure_folders_exist(remote_folders).on_success { hide_inactive_folders(remote_folders) }
end
@@ -169,8 +174,8 @@ def hide_inactive_folders(remote_folders)
info "Hiding folders related to inactive projects"
project_folder_ids = active_project_storages_scope.pluck(:project_folder_id).compact
- remote_folders.except("/#{@storage.group_folder}/").each do |(path, attrs)|
- folder_id = attrs["fileid"]
+ remote_folders.except("/#{@storage.group_folder}").each do |(path, file)|
+ folder_id = file.id
next if project_folder_ids.include?(folder_id)
@@ -196,7 +201,7 @@ def hide_inactive_folders(remote_folders)
def ensure_folders_exist(remote_folders)
info "Ensuring that automatically managed project folders exist and are correctly named."
- id_folder_map = remote_folders.to_h { |folder, properties| [properties["fileid"], folder] }
+ id_folder_map = remote_folders.to_h { |path, file| [file.id, path] }
active_project_storages_scope.includes(:project).map do |project_storage|
unless id_folder_map.key?(project_storage.project_folder_id)
@@ -215,7 +220,7 @@ def ensure_folders_exist(remote_folders)
# @param current_path [String] current name of the remote project storage folder
# @return [ServiceResult, nil]
def rename_folder(project_storage, current_path)
- return if current_path == project_storage.managed_project_folder_path
+ return if UrlBuilder.path(current_path) == UrlBuilder.path(project_storage.managed_project_folder_path)
name = project_storage.managed_project_folder_name
file_id = project_storage.project_folder_id
@@ -283,7 +288,11 @@ def ensure_root_folder_permissions(root_folder_id)
def remote_root_folder_map(group_folder)
info "Retrieving already existing folders under #{group_folder}"
- file_ids.call(storage: @storage, path: group_folder).on_failure do |service_result|
+ file_path_to_id_map.call(storage: @storage,
+ auth_strategy:,
+ folder: Peripherals::ParentFolder.new(group_folder),
+ depth: 1)
+ .on_failure do |service_result|
log_storage_error(service_result.errors, { folder: group_folder })
add_error(:remote_folders, service_result.errors, options: { group_folder:, username: @storage.username }).fail!
end
diff --git a/modules/storages/spec/common/storages/peripherals/registry_spec.rb b/modules/storages/spec/common/storages/peripherals/registry_spec.rb
index 4657f0375011..4f614910bb0e 100644
--- a/modules/storages/spec/common/storages/peripherals/registry_spec.rb
+++ b/modules/storages/spec/common/storages/peripherals/registry_spec.rb
@@ -213,128 +213,6 @@
end
end
- describe "#file_ids_query" do
- let(:nextcloud_subpath) { "" }
- let(:url) { "https://example.com#{nextcloud_subpath}" }
- let(:expected_request_body) do
- <<~XML
-
-
-
-
-
-
- XML
- end
- let(:expected_response_body) do
- <<~XML
-
-
-
- #{nextcloud_subpath}/remote.php/dav/files/OpenProject/OpenProject/
-
-
- 349
-
- HTTP/1.1 200 OK
-
-
-
- #{nextcloud_subpath}/remote.php/dav/files/OpenProject/OpenProject/asd/
-
-
- 783
-
- HTTP/1.1 200 OK
-
-
-
- #{nextcloud_subpath}/remote.php/dav/files/OpenProject/OpenProject/Project%231/
-
-
- 773
-
- HTTP/1.1 200 OK
-
-
-
- #{nextcloud_subpath}/remote.php/dav/files/OpenProject/OpenProject/Project%20%232/
-
-
- 381
-
- HTTP/1.1 200 OK
-
-
-
- #{nextcloud_subpath}/remote.php/dav/files/OpenProject/OpenProject/Project%232/
-
-
- 398
-
- HTTP/1.1 200 OK
-
-
-
- #{nextcloud_subpath}/remote.php/dav/files/OpenProject/OpenProject/qwe/
-
-
- 767
-
- HTTP/1.1 200 OK
-
-
-
- #{nextcloud_subpath}/remote.php/dav/files/OpenProject/OpenProject/qweekk/
-
-
- 802
-
- HTTP/1.1 200 OK
-
-
-
- XML
- end
-
- before do
- stub_request(:propfind, "#{url}/remote.php/dav/files/OpenProject/OpenProject").with(
- body: expected_request_body,
- headers: {
- "Authorization" => "Basic T3BlblByb2plY3Q6T3BlblByb2plY3RTZWN1cmVQYXNzd29yZA==",
- "Depth" => "1"
- }
- ).to_return(status: 200, body: expected_response_body, headers: {})
- end
-
- shared_examples "a file_ids_query response" do
- it "responds with a list of paths and attributes for each of them" do
- result = registry.resolve("nextcloud.queries.file_ids")
- .call(storage:, path: "OpenProject")
- .result
- expect(result).to eq({ "/OpenProject/" => { "fileid" => "349" },
- "/OpenProject/Project #2/" => { "fileid" => "381" },
- "/OpenProject/Project#1/" => { "fileid" => "773" },
- "/OpenProject/Project#2/" => { "fileid" => "398" },
- "/OpenProject/asd/" => { "fileid" => "783" },
- "/OpenProject/qwe/" => { "fileid" => "767" },
- "/OpenProject/qweekk/" => { "fileid" => "802" } })
- end
- end
-
- it_behaves_like "a file_ids_query response"
-
- context "when NC is deployed under subpath" do
- let(:nexcloud_subpath) { "/subpath" }
-
- it_behaves_like "a file_ids_query response"
- end
- end
-
describe "#delete_folder_command" do
let(:auth_strategy) { Storages::Peripherals::StorageInteraction::AuthenticationStrategies::BasicAuth.strategy }
diff --git a/modules/storages/spec/common/storages/peripherals/storage_interaction/nextcloud/file_path_to_id_map_query_spec.rb b/modules/storages/spec/common/storages/peripherals/storage_interaction/nextcloud/file_path_to_id_map_query_spec.rb
index 89e5416a4958..781bf3080012 100644
--- a/modules/storages/spec/common/storages/peripherals/storage_interaction/nextcloud/file_path_to_id_map_query_spec.rb
+++ b/modules/storages/spec/common/storages/peripherals/storage_interaction/nextcloud/file_path_to_id_map_query_spec.rb
@@ -42,26 +42,56 @@
it_behaves_like "file_path_to_id_map_query: basic query setup"
- context "with parent folder being root", vcr: "nextcloud/file_path_to_id_map_query_root" do
+ context "with parent folder being root" do
let(:folder) { Storages::Peripherals::ParentFolder.new("/") }
- let(:expected_ids) do
- {
- "/" => "2",
- "/Folder with spaces" => "165",
- "/Folder with spaces/New Requests" => "166",
- "/Folder with spaces/New Requests/request_001.md" => "167",
- "/Folder with spaces/New Requests/request_002.md" => "168",
- "/Folder" => "169",
- "/Folder/android-studio-2021.3.1.17-linux.tar.gz" => "267",
- "/Folder/empty" => "172",
- "/Folder/Ümlæûts" => "350",
- "/Folder/Ümlæûts/Anrüchiges deutsches Dokument.docx" => "351",
- "/Practical_guide_to_BAGGM_Digital.pdf" => "295",
- "/Readme.md" => "268"
- }
+
+ context "with unset depth (defaults to INFINITY)", vcr: "nextcloud/file_path_to_id_map_query_root_depth_infinite" do
+ let(:expected_ids) do
+ {
+ "/" => "2",
+ "/Folder with spaces" => "165",
+ "/Folder with spaces/New Requests" => "166",
+ "/Folder with spaces/New Requests/I❤️you death star.md" => "167",
+ "/Folder with spaces/New Requests/request_002.md" => "168",
+ "/Folder with spaces/Ümläuts & spe¢iæl characters" => "360",
+ "/Folder with spaces/Ümläuts & spe¢iæl characters/what_have_you_done.md" => "361",
+ "/My files" => "169",
+ "/My files/android-studio-linux.tar.gz" => "267",
+ "/My files/empty" => "172",
+ "/My files/Ümlæûts" => "350",
+ "/My files/Ümlæûts/Anrüchiges deutsches Dokument.docx" => "351",
+ "/Practical_guide_to_BAGGM_Digital.pdf" => "295",
+ "/Readme.md" => "268",
+ "/VCR" => "773",
+ "/VCR/placeholder" => "790"
+ }
+ end
+
+ it_behaves_like "file_path_to_id_map_query: successful query"
end
- it_behaves_like "file_path_to_id_map_query: successful query"
+ context "with depth 0", vcr: "nextcloud/file_path_to_id_map_query_root_depth_0" do
+ let(:depth) { 0 }
+ let(:expected_ids) { { "/" => "2" } }
+
+ it_behaves_like "file_path_to_id_map_query: successful query"
+ end
+
+ context "with depth 1", vcr: "nextcloud/file_path_to_id_map_query_root_depth_1" do
+ let(:depth) { 1 }
+ let(:expected_ids) do
+ {
+ "/" => "2",
+ "/Folder with spaces" => "165",
+ "/My files" => "169",
+ "/Practical_guide_to_BAGGM_Digital.pdf" => "295",
+ "/Readme.md" => "268",
+ "/VCR" => "773"
+ }
+ end
+
+ it_behaves_like "file_path_to_id_map_query: successful query"
+ end
end
context "with a given parent folder", vcr: "nextcloud/file_path_to_id_map_query_parent_folder" do
diff --git a/modules/storages/spec/common/storages/peripherals/storage_interaction/one_drive/file_path_to_id_map_query_spec.rb b/modules/storages/spec/common/storages/peripherals/storage_interaction/one_drive/file_path_to_id_map_query_spec.rb
index 30e21b95feba..b6a157ab4a7e 100644
--- a/modules/storages/spec/common/storages/peripherals/storage_interaction/one_drive/file_path_to_id_map_query_spec.rb
+++ b/modules/storages/spec/common/storages/peripherals/storage_interaction/one_drive/file_path_to_id_map_query_spec.rb
@@ -41,30 +41,54 @@
context "with parent folder being root", vcr: "one_drive/file_path_to_id_map_query_root" do
let(:folder) { Storages::Peripherals::ParentFolder.new("/") }
- let(:expected_ids) do
- {
- "/" => "01AZJL5PN6Y2GOVW7725BZO354PWSELRRZ",
- "/Folder with spaces" => "01AZJL5PKU2WV3U3RKKFF2A7ZCWVBXRTEU",
- "/Folder with spaces/very empty folder" => "01AZJL5PMGEIRPHZPHRRH2NM3D734VIR7H",
- "/Folder with spaces/wordle1.png" => "01AZJL5PPMSBBO3R2BIZHJFCELSW3RP7GN",
- "/Folder with spaces/wordle2.png" => "01AZJL5PIIFUD6A765KBAIAEMYACAFB2WP",
- "/Folder with spaces/wordle3.png" => "01AZJL5PL4AUJEU43CQZFJKN7BQPRP3BLF",
- "/Folder" => "01AZJL5PMAXGDWAAKMEBALX4Q6GSN5BSBR",
- "/Folder/Images" => "01AZJL5PMIF7ND3KH6FVDLZYP3E36ERFGI",
- "/Folder/Subfolder" => "01AZJL5PPWP5UOATNRJJBYJG5TACDHEUAG",
- "/Folder/Ümlæûts" => "01AZJL5PNQYF5NM3KWYNA3RJHJIB2XMMMB",
- "/Folder/Document.docx" => "01AZJL5PJTICED3C5YSVAY6NWTBNA2XERU",
- "/Folder/Sheet.xlsx" => "01AZJL5PLB7SH7633RMBHIH6KVMQRU4RJS",
- "/Folder/Images/der_laufende.jpeg" => "01AZJL5PLZFCARRQIDFJF36UL2WTLXTNSY",
- "/Folder/Images/written_in_stone.webp" => "01AZJL5PLNCKWYI752YBHYYJ6RBFZWOZ46",
- "/Folder/Subfolder/NextcloudHub.md" => "01AZJL5PNCQCEBFI3N7JGZSX5AOX32Z3LA",
- "/Folder/Subfolder/test.txt" => "01AZJL5PLOL2KZTJNVFBCJWFXYGYVBQVMZ",
- "/Folder/Ümlæûts/Anrüchiges deutsches Dokument.docx" => "01AZJL5PNDURPQGKUSGFCJQJMNNWXKTHSE",
- "/Permissions Folder" => "01AZJL5PN3LVLHH2RSZZDJ6ZFAD3OWSGYB"
- }
+
+ context "with unset depth (defaults to INFINITY)" do
+ let(:expected_ids) do
+ {
+ "/" => "01AZJL5PN6Y2GOVW7725BZO354PWSELRRZ",
+ "/Folder with spaces" => "01AZJL5PKU2WV3U3RKKFF2A7ZCWVBXRTEU",
+ "/Folder with spaces/very empty folder" => "01AZJL5PMGEIRPHZPHRRH2NM3D734VIR7H",
+ "/Folder with spaces/wordle1.png" => "01AZJL5PPMSBBO3R2BIZHJFCELSW3RP7GN",
+ "/Folder with spaces/wordle2.png" => "01AZJL5PIIFUD6A765KBAIAEMYACAFB2WP",
+ "/Folder with spaces/wordle3.png" => "01AZJL5PL4AUJEU43CQZFJKN7BQPRP3BLF",
+ "/Folder" => "01AZJL5PMAXGDWAAKMEBALX4Q6GSN5BSBR",
+ "/Folder/Images" => "01AZJL5PMIF7ND3KH6FVDLZYP3E36ERFGI",
+ "/Folder/Subfolder" => "01AZJL5PPWP5UOATNRJJBYJG5TACDHEUAG",
+ "/Folder/Ümlæûts" => "01AZJL5PNQYF5NM3KWYNA3RJHJIB2XMMMB",
+ "/Folder/Document.docx" => "01AZJL5PJTICED3C5YSVAY6NWTBNA2XERU",
+ "/Folder/Sheet.xlsx" => "01AZJL5PLB7SH7633RMBHIH6KVMQRU4RJS",
+ "/Folder/Images/der_laufende.jpeg" => "01AZJL5PLZFCARRQIDFJF36UL2WTLXTNSY",
+ "/Folder/Images/written_in_stone.webp" => "01AZJL5PLNCKWYI752YBHYYJ6RBFZWOZ46",
+ "/Folder/Subfolder/NextcloudHub.md" => "01AZJL5PNCQCEBFI3N7JGZSX5AOX32Z3LA",
+ "/Folder/Subfolder/test.txt" => "01AZJL5PLOL2KZTJNVFBCJWFXYGYVBQVMZ",
+ "/Folder/Ümlæûts/Anrüchiges deutsches Dokument.docx" => "01AZJL5PNDURPQGKUSGFCJQJMNNWXKTHSE",
+ "/Permissions Folder" => "01AZJL5PN3LVLHH2RSZZDJ6ZFAD3OWSGYB"
+ }
+ end
+
+ it_behaves_like "file_path_to_id_map_query: successful query"
end
- it_behaves_like "file_path_to_id_map_query: successful query"
+ context "with a depth of 0" do
+ let(:depth) { 0 }
+ let(:expected_ids) { { "/" => "01AZJL5PN6Y2GOVW7725BZO354PWSELRRZ" } }
+
+ it_behaves_like "file_path_to_id_map_query: successful query"
+ end
+
+ context "with a depth of 1" do
+ let(:depth) { 1 }
+ let(:expected_ids) do
+ {
+ "/" => "01AZJL5PN6Y2GOVW7725BZO354PWSELRRZ",
+ "/Folder with spaces" => "01AZJL5PKU2WV3U3RKKFF2A7ZCWVBXRTEU",
+ "/Folder" => "01AZJL5PMAXGDWAAKMEBALX4Q6GSN5BSBR",
+ "/Permissions Folder" => "01AZJL5PN3LVLHH2RSZZDJ6ZFAD3OWSGYB"
+ }
+ end
+
+ it_behaves_like "file_path_to_id_map_query: successful query"
+ end
end
context "with a given parent folder", vcr: "one_drive/file_path_to_id_map_query_parent_folder" do
diff --git a/modules/storages/spec/services/storages/nextcloud_managed_folder_sync_service_spec.rb b/modules/storages/spec/services/storages/nextcloud_managed_folder_sync_service_spec.rb
index 68f90093dcb2..ee1a1a10edbe 100644
--- a/modules/storages/spec/services/storages/nextcloud_managed_folder_sync_service_spec.rb
+++ b/modules/storages/spec/services/storages/nextcloud_managed_folder_sync_service_spec.rb
@@ -55,10 +55,14 @@ module Storages
shared_let(:remote_identities) do
[create(:remote_identity, user: admin, oauth_client: storage.oauth_client, origin_user_id: "admin"),
- create(:remote_identity, user: multiple_projects_user, oauth_client: storage.oauth_client,
- origin_user_id: "multiple_projects_user"),
- create(:remote_identity, user: single_project_user, oauth_client: storage.oauth_client,
- origin_user_id: "single_project_user")]
+ create(:remote_identity,
+ user: multiple_projects_user,
+ oauth_client: storage.oauth_client,
+ origin_user_id: "multiple_projects_user"),
+ create(:remote_identity,
+ user: single_project_user,
+ oauth_client: storage.oauth_client,
+ origin_user_id: "single_project_user")]
end
shared_let(:non_member_role) { create(:non_member, permissions: ["read_files"]) }
@@ -69,12 +73,14 @@ module Storages
create(:project, :archived, name: "INACTIVE PROJECT", members: { multiple_projects_user => ordinary_role })
end
shared_let(:project) do
- create(:project, name: "[Sample] Project Name / Ehüu ///",
- members: { multiple_projects_user => ordinary_role, single_project_user => ordinary_role })
+ create(:project,
+ name: "[Sample] Project Name / Ehüu ///",
+ members: { multiple_projects_user => ordinary_role, single_project_user => ordinary_role })
end
shared_let(:renamed_project) do
- create(:project, name: "Renamed Project #23",
- members: { multiple_projects_user => ordinary_role })
+ create(:project,
+ name: "Renamed Project #23",
+ members: { multiple_projects_user => ordinary_role })
end
let!(:public_storage) { create(:project_storage, :as_automatically_managed, storage:, project: public_project) }
@@ -89,7 +95,7 @@ module Storages
storage:, project: renamed_project, project_folder_id: "9001")
end
- let(:file_ids) { class_double(Peripherals::StorageInteraction::Nextcloud::FileIdsQuery) }
+ let(:file_path_to_id_map) { class_double(Peripherals::StorageInteraction::Nextcloud::FilePathToIdMapQuery) }
let(:group_users) { class_double(Peripherals::StorageInteraction::Nextcloud::GroupUsersQuery) }
let(:rename_file) { class_double(Peripherals::StorageInteraction::Nextcloud::RenameFileCommand) }
let(:set_permissions) { class_double(Peripherals::StorageInteraction::Nextcloud::SetPermissionsCommand) }
@@ -99,12 +105,14 @@ module Storages
let(:auth_strategy) { Peripherals::StorageInteraction::AuthenticationStrategies::Strategy.new(key: :basic_auth) }
let(:root_folder_id) { "root_folder_id" }
- let(:file_ids_result) do
+ let(:file_path_to_id_map_result) do
+ inactive_storage_path = inactive_storage.managed_project_folder_path.chomp("/")
+
ServiceResult.success(
result: {
- "/OpenProject/" => { "fileid" => root_folder_id },
- inactive_storage.managed_project_folder_path => { "fileid" => inactive_storage.project_folder_id },
- "/OpenProject/Another Name for this Project/" => { "fileid" => renamed_storage.project_folder_id }
+ "/OpenProject" => StorageFileId.new(root_folder_id),
+ inactive_storage_path => StorageFileId.new(inactive_storage.project_folder_id),
+ "/OpenProject/Another Name for this Project" => StorageFileId.new(renamed_storage.project_folder_id)
}
)
end
@@ -138,7 +146,7 @@ module Storages
let(:create_folder_result) { build_create_folder_result }
before do
- Peripherals::Registry.stub("nextcloud.queries.file_ids", file_ids)
+ Peripherals::Registry.stub("nextcloud.queries.file_path_to_id_map", file_path_to_id_map)
Peripherals::Registry.stub("nextcloud.queries.group_users", group_users)
Peripherals::Registry.stub("nextcloud.commands.add_user_to_group", add_user)
Peripherals::Registry.stub("nextcloud.commands.create_folder", create_folder)
@@ -148,7 +156,10 @@ module Storages
Peripherals::Registry.stub("nextcloud.authentication.userless", -> { auth_strategy })
# We arent using ParentFolder nor AuthStrategies on FileIds
- allow(file_ids).to receive(:call).with(storage:, path: storage.group).and_return(file_ids_result)
+ folder = Peripherals::ParentFolder.new(storage.group)
+ allow(file_path_to_id_map).to receive(:call).with(storage:, auth_strategy:, folder:, depth: 1)
+ .and_return(file_path_to_id_map_result)
+
# Setting the Group Permissions
allow(set_permissions).to receive(:call).with(storage:, auth_strategy:, input_data: root_permission_input)
.and_return(root_permissions_result)
@@ -191,11 +202,11 @@ module Storages
end
context "when a project is renamed" do
- let(:file_ids_result) do
+ let(:file_path_to_id_map_result) do
ServiceResult.success(
result: {
- "/OpenProject/" => { "fileid" => root_folder_id },
- "/OpenProject/OBVIOUSLY NON RENAMED/" => { "fileid" => renamed_storage.project_folder_id }
+ "/OpenProject" => StorageFileId.new(root_folder_id),
+ "/OpenProject/OBVIOUSLY NON RENAMED" => StorageFileId.new(renamed_storage.project_folder_id)
}
)
end
@@ -221,7 +232,9 @@ module Storages
end
context "with a public project" do
- let(:file_ids_result) { ServiceResult.success(result: { "/OpenProject/" => { "fileid" => root_folder_id } }) }
+ let(:file_path_to_id_map_result) do
+ ServiceResult.success(result: { "/OpenProject" => StorageFileId.new(root_folder_id) })
+ end
before { ProjectStorage.where.not(id: public_storage.id).delete_all }
@@ -260,8 +273,10 @@ module Storages
let(:error_prefix) { "services.errors.models.nextcloud_sync_service" }
context "when the initial fetch of remote folders fails" do
- let(:file_ids_result) do
- errors = storage_error(:unauthorized, "error body", Peripherals::StorageInteraction::Nextcloud::FileIdsQuery)
+ let(:file_path_to_id_map_result) do
+ errors = storage_error(:unauthorized,
+ "error body",
+ Peripherals::StorageInteraction::Nextcloud::FilePathToIdMapQuery)
ServiceResult.failure(result: :unauthorized, errors:)
end
@@ -350,7 +365,8 @@ module Storages
end
it "interrupts the flow" do
- commands = [file_ids, set_permissions, group_users, add_user, create_folder, remove_user, rename_file]
+ commands = [file_path_to_id_map, set_permissions, group_users, add_user, create_folder, remove_user,
+ rename_file]
service.call(storage)
expect(commands).to all(have_received(:call).at_least(:once))
end
@@ -367,10 +383,12 @@ def storage_error(code, data, source)
def build_create_folder_result
{
- public_storage.managed_project_folder_name => ServiceResult.success(result:
- StorageFile.new(id: "public_id", name: public_storage.managed_project_folder_name)),
- project_storage.managed_project_folder_name => ServiceResult.success(result:
- StorageFile.new(id: "normal_project_id", name: project_storage.managed_project_folder_name))
+ public_storage.managed_project_folder_name =>
+ ServiceResult.success(result: StorageFile.new(id: "public_id",
+ name: public_storage.managed_project_folder_name)),
+ project_storage.managed_project_folder_name =>
+ ServiceResult.success(result: StorageFile.new(id: "normal_project_id",
+ name: project_storage.managed_project_folder_name))
}
end
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root.yml
deleted file mode 100644
index 562e2ad833f8..000000000000
--- a/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root.yml
+++ /dev/null
@@ -1,98 +0,0 @@
----
-http_interactions:
-- request:
- method: propfind
- uri: https://nextcloud.local/remote.php/dav/files/admin
- body:
- encoding: UTF-8
- string: |
-
-
-
-
-
-
-
- headers:
- Depth:
- - infinity
- Authorization:
- - Bearer
- User-Agent:
- - httpx.rb/1.2.4
- Accept:
- - "*/*"
- Accept-Encoding:
- - gzip, deflate
- Content-Type:
- - application/xml; charset=utf-8
- Content-Length:
- - '192'
- response:
- status:
- code: 207
- message: Multi-Status
- headers:
- Cache-Control:
- - no-store, no-cache, must-revalidate
- Content-Encoding:
- - gzip
- Content-Security-Policy:
- - default-src 'none';
- Content-Type:
- - application/xml; charset=utf-8
- Date:
- - Mon, 29 Apr 2024 09:21:37 GMT
- Dav:
- - 1, 3, extended-mkcol, access-control, calendarserver-principal-property-search,
- nextcloud-checksum-update, nc-calendar-search, nc-enable-birthday-calendar
- Expires:
- - Thu, 19 Nov 1981 08:52:00 GMT
- Pragma:
- - no-cache
- Referrer-Policy:
- - no-referrer
- Server:
- - Apache/2.4.59 (Debian)
- Set-Cookie:
- - oc07ul6b4oaw=dce80de6ee27126cc7f063c499bbf2d2; path=/; secure; HttpOnly; SameSite=Lax,
- oc_sessionPassphrase=qHbGrRqzApLz65yOgbjo0F50qZOP7tl5seLjGa5MF5xAsfWUBS86U4TbnP6wBuwrqH1oAgAR19gOq6FYJvymPZW3tkMSKR9kS5YaIuyIWuqjif2txirDkqaymU7cOOVG;
- path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=dce80de6ee27126cc7f063c499bbf2d2;
- path=/; secure; HttpOnly; SameSite=Lax, __Host-nc_sameSiteCookielax=true;
- path=/; httponly;secure; expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=lax,
- __Host-nc_sameSiteCookiestrict=true; path=/; httponly;secure; expires=Fri,
- 31-Dec-2100 23:59:59 GMT; SameSite=strict, oc07ul6b4oaw=dce80de6ee27126cc7f063c499bbf2d2;
- path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=dce80de6ee27126cc7f063c499bbf2d2;
- path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=dce80de6ee27126cc7f063c499bbf2d2;
- path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=dce80de6ee27126cc7f063c499bbf2d2;
- path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=dce80de6ee27126cc7f063c499bbf2d2;
- path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=dce80de6ee27126cc7f063c499bbf2d2;
- path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=dce80de6ee27126cc7f063c499bbf2d2;
- path=/; secure; HttpOnly; SameSite=Lax
- Vary:
- - Brief,Prefer
- X-Content-Type-Options:
- - nosniff
- X-Debug-Token:
- - 6Cb1TBf2Ullw34vWD3yf
- X-Frame-Options:
- - SAMEORIGIN
- X-Permitted-Cross-Domain-Policies:
- - none
- X-Powered-By:
- - PHP/8.2.18
- X-Request-Id:
- - 6Cb1TBf2Ullw34vWD3yf
- X-Robots-Tag:
- - noindex, nofollow
- X-Xss-Protection:
- - 1; mode=block
- Content-Length:
- - '477'
- body:
- encoding: UTF-8
- string: |
-
- /remote.php/dav/files/admin/2HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder/169HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder/android-studio-2021.3.1.17-linux.tar.gz267HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder/empty/172HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder/%c3%9cml%c3%a6%c3%bbts/350HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder/%c3%9cml%c3%a6%c3%bbts/Anr%c3%bcchiges%20deutsches%20Dokument.docx351HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder%20with%20spaces/165HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder%20with%20spaces/New%20Requests/166HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder%20with%20spaces/New%20Requests/request_001.md167HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder%20with%20spaces/New%20Requests/request_002.md168HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Practical_guide_to_BAGGM_Digital.pdf295HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Readme.md268HTTP/1.1 200 OKHTTP/1.1 404 Not Found
- recorded_at: Mon, 29 Apr 2024 09:21:38 GMT
-recorded_with: VCR 6.2.0
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root_depth_0.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root_depth_0.yml
new file mode 100644
index 000000000000..a6ee967ab950
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root_depth_0.yml
@@ -0,0 +1,98 @@
+---
+http_interactions:
+- request:
+ method: propfind
+ uri: https://nextcloud.local/remote.php/dav/files/admin
+ body:
+ encoding: UTF-8
+ string: |
+
+
+
+
+
+
+
+ headers:
+ Depth:
+ - '0'
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.1
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/xml; charset=utf-8
+ Content-Length:
+ - '192'
+ response:
+ status:
+ code: 207
+ message: Multi-Status
+ headers:
+ Cache-Control:
+ - no-store, no-cache, must-revalidate
+ Content-Encoding:
+ - gzip
+ Content-Security-Policy:
+ - default-src 'none';
+ Content-Type:
+ - application/xml; charset=utf-8
+ Date:
+ - Mon, 09 Sep 2024 14:06:46 GMT
+ Dav:
+ - 1, 3, extended-mkcol, access-control, calendarserver-principal-property-search,
+ nextcloud-checksum-update, nc-calendar-search, nc-enable-birthday-calendar
+ Expires:
+ - Thu, 19 Nov 1981 08:52:00 GMT
+ Pragma:
+ - no-cache
+ Referrer-Policy:
+ - no-referrer
+ Server:
+ - Apache/2.4.59 (Debian)
+ Set-Cookie:
+ - oc07ul6b4oaw=0e2bf236478ffa7e206813fcffc10e28; path=/; secure; HttpOnly; SameSite=Lax,
+ oc_sessionPassphrase=JfSm9aY2oVbQ8US%2BXKTpHqyK5QRr6DP2otXqhoqXfhj8xJylyuwTTw4e2RkNlGalL4NVCyy9aT9vrISN5lf563XTkeVliDatBMabXlUn67CvF%2Byg%2BTuXTTo%2FlyMdeg5d;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=0e2bf236478ffa7e206813fcffc10e28;
+ path=/; secure; HttpOnly; SameSite=Lax, __Host-nc_sameSiteCookielax=true;
+ path=/; httponly;secure; expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=lax,
+ __Host-nc_sameSiteCookiestrict=true; path=/; httponly;secure; expires=Fri,
+ 31-Dec-2100 23:59:59 GMT; SameSite=strict, oc07ul6b4oaw=0e2bf236478ffa7e206813fcffc10e28;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=0e2bf236478ffa7e206813fcffc10e28;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=0e2bf236478ffa7e206813fcffc10e28;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=0e2bf236478ffa7e206813fcffc10e28;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=0e2bf236478ffa7e206813fcffc10e28;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=0e2bf236478ffa7e206813fcffc10e28;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=0e2bf236478ffa7e206813fcffc10e28;
+ path=/; secure; HttpOnly; SameSite=Lax
+ Vary:
+ - Brief,Prefer
+ X-Content-Type-Options:
+ - nosniff
+ X-Debug-Token:
+ - mEyWbELH6q2kJulQSYjA
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ X-Powered-By:
+ - PHP/8.2.21
+ X-Request-Id:
+ - mEyWbELH6q2kJulQSYjA
+ X-Robots-Tag:
+ - noindex, nofollow
+ X-Xss-Protection:
+ - 1; mode=block
+ Content-Length:
+ - '244'
+ body:
+ encoding: UTF-8
+ string: |
+
+ /remote.php/dav/files/admin/2HTTP/1.1 200 OKHTTP/1.1 404 Not Found
+ recorded_at: Mon, 09 Sep 2024 14:06:47 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root_depth_1.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root_depth_1.yml
new file mode 100644
index 000000000000..c2d711e9f897
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root_depth_1.yml
@@ -0,0 +1,98 @@
+---
+http_interactions:
+- request:
+ method: propfind
+ uri: https://nextcloud.local/remote.php/dav/files/admin
+ body:
+ encoding: UTF-8
+ string: |
+
+
+
+
+
+
+
+ headers:
+ Depth:
+ - '1'
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.1
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/xml; charset=utf-8
+ Content-Length:
+ - '192'
+ response:
+ status:
+ code: 207
+ message: Multi-Status
+ headers:
+ Cache-Control:
+ - no-store, no-cache, must-revalidate
+ Content-Encoding:
+ - gzip
+ Content-Security-Policy:
+ - default-src 'none';
+ Content-Type:
+ - application/xml; charset=utf-8
+ Date:
+ - Mon, 09 Sep 2024 14:06:47 GMT
+ Dav:
+ - 1, 3, extended-mkcol, access-control, calendarserver-principal-property-search,
+ nextcloud-checksum-update, nc-calendar-search, nc-enable-birthday-calendar
+ Expires:
+ - Thu, 19 Nov 1981 08:52:00 GMT
+ Pragma:
+ - no-cache
+ Referrer-Policy:
+ - no-referrer
+ Server:
+ - Apache/2.4.59 (Debian)
+ Set-Cookie:
+ - oc07ul6b4oaw=f663195643887b49aa2e556e7d87c05f; path=/; secure; HttpOnly; SameSite=Lax,
+ oc_sessionPassphrase=si2bSDRidz74z0Y8C3%2Bxrcse79aZZyLN6mIITel6H2PgF6i%2FJzFSs%2BAN23RJAfN9hlu%2B%2BpP5H2Y6Sv8iF221RcPRjB0ba9GdjJQa2wSiBWgbX3mBx3hqFlKB4qsGr2ah;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=f663195643887b49aa2e556e7d87c05f;
+ path=/; secure; HttpOnly; SameSite=Lax, __Host-nc_sameSiteCookielax=true;
+ path=/; httponly;secure; expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=lax,
+ __Host-nc_sameSiteCookiestrict=true; path=/; httponly;secure; expires=Fri,
+ 31-Dec-2100 23:59:59 GMT; SameSite=strict, oc07ul6b4oaw=f663195643887b49aa2e556e7d87c05f;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=f663195643887b49aa2e556e7d87c05f;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=f663195643887b49aa2e556e7d87c05f;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=f663195643887b49aa2e556e7d87c05f;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=f663195643887b49aa2e556e7d87c05f;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=f663195643887b49aa2e556e7d87c05f;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=f663195643887b49aa2e556e7d87c05f;
+ path=/; secure; HttpOnly; SameSite=Lax
+ Vary:
+ - Brief,Prefer
+ X-Content-Type-Options:
+ - nosniff
+ X-Debug-Token:
+ - f0xXuPYZTmTW8miDlwYf
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ X-Powered-By:
+ - PHP/8.2.21
+ X-Request-Id:
+ - f0xXuPYZTmTW8miDlwYf
+ X-Robots-Tag:
+ - noindex, nofollow
+ X-Xss-Protection:
+ - 1; mode=block
+ Content-Length:
+ - '427'
+ body:
+ encoding: UTF-8
+ string: |
+
+ /remote.php/dav/files/admin/2HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder%20with%20spaces/165HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/My%20files/169HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Practical_guide_to_BAGGM_Digital.pdf295HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Readme.md268HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/VCR/773uservcrvcr3131HTTP/1.1 200 OK
+ recorded_at: Mon, 09 Sep 2024 14:06:47 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root_depth_infinite.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root_depth_infinite.yml
new file mode 100644
index 000000000000..2329bfda2bb5
--- /dev/null
+++ b/modules/storages/spec/support/fixtures/vcr_cassettes/nextcloud/file_path_to_id_map_query_root_depth_infinite.yml
@@ -0,0 +1,98 @@
+---
+http_interactions:
+- request:
+ method: propfind
+ uri: https://nextcloud.local/remote.php/dav/files/admin
+ body:
+ encoding: UTF-8
+ string: |
+
+
+
+
+
+
+
+ headers:
+ Depth:
+ - infinity
+ Authorization:
+ - Bearer
+ User-Agent:
+ - httpx.rb/1.3.1
+ Accept:
+ - "*/*"
+ Accept-Encoding:
+ - gzip, deflate
+ Content-Type:
+ - application/xml; charset=utf-8
+ Content-Length:
+ - '192'
+ response:
+ status:
+ code: 207
+ message: Multi-Status
+ headers:
+ Cache-Control:
+ - no-store, no-cache, must-revalidate
+ Content-Encoding:
+ - gzip
+ Content-Security-Policy:
+ - default-src 'none';
+ Content-Type:
+ - application/xml; charset=utf-8
+ Date:
+ - Mon, 09 Sep 2024 14:04:03 GMT
+ Dav:
+ - 1, 3, extended-mkcol, access-control, calendarserver-principal-property-search,
+ nextcloud-checksum-update, nc-calendar-search, nc-enable-birthday-calendar
+ Expires:
+ - Thu, 19 Nov 1981 08:52:00 GMT
+ Pragma:
+ - no-cache
+ Referrer-Policy:
+ - no-referrer
+ Server:
+ - Apache/2.4.59 (Debian)
+ Set-Cookie:
+ - oc07ul6b4oaw=71064cc5df3a5e7bd8d9179c340c4dab; path=/; secure; HttpOnly; SameSite=Lax,
+ oc_sessionPassphrase=H%2BVMPNSuNA3ELOSV68a9AG722Qq%2Fs09dgww4ZYabMV0YHZyv842kM5QuOLv6lCMEl42anma5TpUAbJHW0wrR2dc7%2BAqBKdwwFwL6rGEfXPqtbAz%2BeON8ybKhnzw0tbn7;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=71064cc5df3a5e7bd8d9179c340c4dab;
+ path=/; secure; HttpOnly; SameSite=Lax, __Host-nc_sameSiteCookielax=true;
+ path=/; httponly;secure; expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=lax,
+ __Host-nc_sameSiteCookiestrict=true; path=/; httponly;secure; expires=Fri,
+ 31-Dec-2100 23:59:59 GMT; SameSite=strict, oc07ul6b4oaw=71064cc5df3a5e7bd8d9179c340c4dab;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=71064cc5df3a5e7bd8d9179c340c4dab;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=71064cc5df3a5e7bd8d9179c340c4dab;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=71064cc5df3a5e7bd8d9179c340c4dab;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=71064cc5df3a5e7bd8d9179c340c4dab;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=71064cc5df3a5e7bd8d9179c340c4dab;
+ path=/; secure; HttpOnly; SameSite=Lax, oc07ul6b4oaw=71064cc5df3a5e7bd8d9179c340c4dab;
+ path=/; secure; HttpOnly; SameSite=Lax
+ Vary:
+ - Brief,Prefer
+ X-Content-Type-Options:
+ - nosniff
+ X-Debug-Token:
+ - tHdjNhNVN1e8HHQSUi3w
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ X-Powered-By:
+ - PHP/8.2.21
+ X-Request-Id:
+ - tHdjNhNVN1e8HHQSUi3w
+ X-Robots-Tag:
+ - noindex, nofollow
+ X-Xss-Protection:
+ - 1; mode=block
+ Content-Length:
+ - '666'
+ body:
+ encoding: UTF-8
+ string: |
+
+ /remote.php/dav/files/admin/2HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder%20with%20spaces/165HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder%20with%20spaces/New%20Requests/166HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder%20with%20spaces/New%20Requests/I%e2%9d%a4%ef%b8%8fyou%20death%20star.md167HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder%20with%20spaces/New%20Requests/request_002.md168HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder%20with%20spaces/%c3%9cml%c3%a4uts%20%26%20spe%c2%a2i%c3%a6l%20characters/360HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Folder%20with%20spaces/%c3%9cml%c3%a4uts%20%26%20spe%c2%a2i%c3%a6l%20characters/what_have_you_done.md361HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/My%20files/169HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/My%20files/android-studio-linux.tar.gz267HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/My%20files/empty/172HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/My%20files/%c3%9cml%c3%a6%c3%bbts/350HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/My%20files/%c3%9cml%c3%a6%c3%bbts/Anr%c3%bcchiges%20deutsches%20Dokument.docx351HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Practical_guide_to_BAGGM_Digital.pdf295HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/Readme.md268HTTP/1.1 200 OKHTTP/1.1 404 Not Found/remote.php/dav/files/admin/VCR/773uservcrvcr3131HTTP/1.1 200 OK/remote.php/dav/files/admin/VCR/placeholder/790HTTP/1.1 200 OKHTTP/1.1 404 Not Found
+ recorded_at: Mon, 09 Sep 2024 14:04:03 GMT
+recorded_with: VCR 6.3.1
diff --git a/modules/storages/spec/support/shared_examples_for_adapters/file_path_to_id_map_query_examples.rb b/modules/storages/spec/support/shared_examples_for_adapters/file_path_to_id_map_query_examples.rb
index d609e50588ed..6abec5e19218 100644
--- a/modules/storages/spec/support/shared_examples_for_adapters/file_path_to_id_map_query_examples.rb
+++ b/modules/storages/spec/support/shared_examples_for_adapters/file_path_to_id_map_query_examples.rb
@@ -40,13 +40,18 @@
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage],
%i[keyreq auth_strategy],
- %i[keyreq folder])
+ %i[keyreq folder],
+ %i[key depth])
end
end
RSpec.shared_examples_for "file_path_to_id_map_query: successful query" do
it "returns a map of locations to file ids" do
- result = described_class.call(storage:, auth_strategy:, folder:)
+ result = if defined?(depth)
+ described_class.call(storage:, auth_strategy:, folder:, depth:)
+ else
+ described_class.call(storage:, auth_strategy:, folder:)
+ end
expect(result).to be_success