Skip to content

Commit

Permalink
Merge pull request #15873 from opf/chore/add-regression-test-for-fold…
Browse files Browse the repository at this point in the history
…ers-with-ampersands

[chore] Add regression tests for storage folders
  • Loading branch information
Kharonus authored Jun 18, 2024
2 parents 040fa72 + 6aa096a commit 2d0993f
Show file tree
Hide file tree
Showing 21 changed files with 878 additions and 594 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ module Storages
module Peripherals
module StorageInteraction
class Authentication
using ::Storages::Peripherals::ServiceResultRefinements
using ServiceResultRefinements

def self.[](strategy)
case strategy.key
Expand All @@ -55,7 +55,7 @@ def self.[](strategy)
def self.authorization_state(storage:, user:)
auth_strategy = AuthenticationStrategies::OAuthUserToken.strategy.with_user(user)

::Storages::Peripherals::Registry
Registry
.resolve("#{storage.short_provider_type}.queries.auth_check")
.call(storage:, auth_strategy:)
.match(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,107 +28,122 @@
# See COPYRIGHT and LICENSE files for more details.
#++

module Storages::Peripherals::StorageInteraction::Nextcloud
class FileInfoQuery
using Storages::Peripherals::ServiceResultRefinements

FILE_INFO_PATH = "ocs/v1.php/apps/integration_openproject/fileinfo"
Auth = ::Storages::Peripherals::StorageInteraction::Authentication

def self.call(storage:, auth_strategy:, file_id:)
new(storage).call(auth_strategy:, file_id:)
end

def initialize(storage)
@storage = storage
end

def call(auth_strategy:, file_id:)
http_options = Util.ocs_api_request.deep_merge(Util.accept_json)
Auth[auth_strategy].call(storage: @storage, http_options:) do |http|
file_info(http, file_id).map(&parse_json) >> handle_failure >> create_storage_file_info
end
end

private

def file_info(http, file_id)
response = http.get(Util.join_uri_path(@storage.uri, FILE_INFO_PATH, file_id))
error_data = Storages::StorageErrorData.new(source: self.class, payload: response)

case response
in { status: 200..299 }
ServiceResult.success(result: response.body)
in { status: 404 }
Util.error(:not_found, "Outbound request destination not found!", error_data)
in { status: 401 }
Util.error(:unauthorized, "Outbound request not authorized!", error_data)
else
Util.error(:error, "Outbound request failed!", error_data)
end
end

def parse_json
->(response_body) do
# rubocop:disable Style/OpenStructUse
JSON.parse(response_body, object_class: OpenStruct)
# rubocop:enable Style/OpenStructUse
end
end

def handle_failure
->(response_object) do
error_data = Storages::StorageErrorData.new(source: self.class, payload: response_object)

case response_object.ocs.data.statuscode
when 200..299
ServiceResult.success(result: response_object)
when 403
Util.error(:forbidden, "Access to storage file forbidden!", error_data)
when 404
Util.error(:not_found, "Storage file not found!", error_data)
else
Util.error(:error, "Outbound request failed!", error_data)
module Storages
module Peripherals
module StorageInteraction
module Nextcloud
class FileInfoQuery
using ServiceResultRefinements

FILE_INFO_PATH = "ocs/v1.php/apps/integration_openproject/fileinfo"

def self.call(storage:, auth_strategy:, file_id:)
new(storage).call(auth_strategy:, file_id:)
end

def initialize(storage)
@storage = storage
end

def call(auth_strategy:, file_id:)
validation = validate_input(file_id)
return validation if validation.failure?

http_options = Util.ocs_api_request.deep_merge(Util.accept_json)
Authentication[auth_strategy].call(storage: @storage, http_options:) do |http|
file_info(http, file_id).map(&parse_json) >> handle_failure >> create_storage_file_info
end
end

private

def validate_input(file_id)
if file_id.nil?
ServiceResult.failure(
result: :error,
errors: StorageError.new(code: :error,
data: StorageErrorData.new(source: self.class),
log_message: "File ID can not be nil")
)
else
ServiceResult.success
end
end

def file_info(http, file_id)
response = http.get(Util.join_uri_path(@storage.uri, FILE_INFO_PATH, file_id))
error_data = StorageErrorData.new(source: self.class, payload: response)

case response
in { status: 200..299 }
ServiceResult.success(result: response.body)
in { status: 404 }
Util.error(:not_found, "Outbound request destination not found!", error_data)
in { status: 401 }
Util.error(:unauthorized, "Outbound request not authorized!", error_data)
else
Util.error(:error, "Outbound request failed!", error_data)
end
end

def parse_json
->(response_body) do
JSON.parse(response_body, object_class: OpenStruct) # rubocop:disable Style/OpenStructUse
end
end

def handle_failure
->(response_object) do
error_data = StorageErrorData.new(source: self.class, payload: response_object)

case response_object.ocs.data.statuscode
when 200..299
ServiceResult.success(result: response_object)
when 403
Util.error(:forbidden, "Access to storage file forbidden!", error_data)
when 404
Util.error(:not_found, "Storage file not found!", error_data)
else
Util.error(:error, "Outbound request failed!", error_data)
end
end
end

def create_storage_file_info # rubocop:disable Metrics/AbcSize
->(response_object) do
data = response_object.ocs.data
ServiceResult.success(
result: StorageFileInfo.new(
status: data.status.downcase,
status_code: data.statuscode,
id: data.id.to_s,
name: data.name,
last_modified_at: Time.zone.at(data.mtime),
created_at: Time.zone.at(data.ctime),
mime_type: data.mimetype,
size: data.size,
owner_name: data.owner_name,
owner_id: data.owner_id,
last_modified_by_name: data.modifier_name,
last_modified_by_id: data.modifier_id,
permissions: data.dav_permissions,
location: location(data.path)
)
)
end
end

def location(file_path)
prefix = "files/"
idx = file_path.rindex(prefix)
return "/" if idx == nil

idx += prefix.length - 1

Util.escape_path(file_path[idx..]).chomp("/")
end
end
end
end

# rubocop:disable Metrics/AbcSize
def create_storage_file_info
->(response_object) do
data = response_object.ocs.data
ServiceResult.success(
result: ::Storages::StorageFileInfo.new(
status: data.status,
status_code: data.statuscode,
id: data.id.to_s,
name: data.name,
last_modified_at: Time.zone.at(data.mtime),
created_at: Time.zone.at(data.ctime),
mime_type: data.mimetype,
size: data.size,
owner_name: data.owner_name,
owner_id: data.owner_id,
trashed: data.trashed,
last_modified_by_name: data.modifier_name,
last_modified_by_id: data.modifier_id,
permissions: data.dav_permissions,
location: location(data.path)
)
)
end
end

# rubocop:enable Metrics/AbcSize

def location(file_path)
prefix = "files/"
idx = file_path.rindex(prefix)
return "/" if idx == nil

idx += prefix.length - 1

Util.escape_path(file_path[idx..])
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ def create_storage_file_infos
size: value.size,
owner_name: value.owner_name,
owner_id: value.owner_id,
trashed: value.trashed,
last_modified_by_name: value.modifier_name,
last_modified_by_id: value.modifier_id,
permissions: value.dav_permissions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,14 @@ def initialize(storage)
end

def call(auth_strategy:, file_id:)
if file_id.nil?
return ServiceResult.failure(
result: :error,
errors: StorageError.new(code: :error,
data: @error_data, log_message: "File ID can not be nil")
)
end
validation = validate_input(file_id)
return validation if validation.failure?

requested_result = Authentication[auth_strategy].call(storage: @storage) do |http|
@drive_item_query.call(http:, drive_item_id: file_id, fields: FIELDS)
end

requested_result.on_success { |sr| return ServiceResult.success(result: storage_file_infos(sr.result)) }
requested_result.on_success { |sr| return ServiceResult.success(result: storage_file_info(sr.result)) }
requested_result.on_failure do |sr|
return sr unless sr.result == :not_found && auth_strategy.user.present?

Expand All @@ -75,14 +70,26 @@ def admin_query(file_id)

admin_result.on_success do |admin_query|
return ServiceResult.success(
result: storage_file_infos(admin_query.result, status: "forbidden", status_code: 403)
result: storage_file_info(admin_query.result, status: "forbidden", status_code: 403)
)
end
end

def validate_input(file_id)
if file_id.nil?
ServiceResult.failure(
result: :error,
errors: StorageError.new(code: :error,
data: @error_data, log_message: "File ID can not be nil")
)
else
ServiceResult.success
end
end

def userless_strategy = Registry.resolve("one_drive.authentication.userless").call

def storage_file_infos(json, status: "ok", status_code: 200) # rubocop:disable Metrics/AbcSize
def storage_file_info(json, status: "ok", status_code: 200) # rubocop:disable Metrics/AbcSize
StorageFileInfo.new(
status:,
status_code:,
Expand All @@ -92,7 +99,6 @@ def storage_file_infos(json, status: "ok", status_code: 200) # rubocop:disable M
size: json[:size],
owner_name: json.dig(:createdBy, :user, :displayName),
owner_id: json.dig(:createdBy, :user, :id),
trashed: false,
permissions: nil,
location: Util.escape_path(Util.extract_location(json[:parentReference], json[:name])),
last_modified_at: Time.zone.parse(json.dig(:fileSystemInfo, :lastModifiedDateTime)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def call(auth_strategy:, file_ids:)
if file_ids.nil?
return ServiceResult.failure(
result: :error,
errors: ::Storages::StorageError.new(code: :error, log_message: "File IDs can not be nil")
errors: StorageError.new(code: :error, log_message: "File IDs can not be nil")
)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ module StorageInteraction
module OneDrive
class FilesQuery
FIELDS = "?$select=id,name,size,webUrl,lastModifiedBy,createdBy,fileSystemInfo,file,folder,parentReference"
Auth = ::Storages::Peripherals::StorageInteraction::Authentication

using ServiceResultRefinements

Expand All @@ -48,7 +47,7 @@ def initialize(storage)
end

def call(auth_strategy:, folder:)
Auth[auth_strategy].call(storage: @storage, http_options: { headers: { "OCS-APIRequest" => "true" } }) do |http|
Authentication[auth_strategy].call(storage: @storage) do |http|
call = http.get(Util.join_uri_path(@uri, children_uri_path_for(folder) + FIELDS))
response = handle_response(call, :value)

Expand Down Expand Up @@ -76,8 +75,8 @@ def handle_response(response, map_value)
ServiceResult.failure(result: :unauthorized,
errors: Util.storage_error(response:, code: :unauthorized, source: self.class))
else
data = ::Storages::StorageErrorData.new(source: self.class, payload: response)
ServiceResult.failure(result: :error, errors: ::Storages::StorageError.new(code: :error, data:))
data = StorageErrorData.new(source: self.class, payload: response)
ServiceResult.failure(result: :error, errors: StorageError.new(code: :error, data:))
end
end

Expand All @@ -98,7 +97,7 @@ def storage_file(json_file)
last_modified_at: Time.zone.parse(json_file.dig(:fileSystemInfo, :lastModifiedDateTime)),
created_by_name: json_file.dig(:createdBy, :user, :displayName),
last_modified_by_name: json_file.dig(:lastModifiedBy, :user, :displayName),
location: Util.extract_location(json_file[:parentReference], json_file[:name]),
location: Util.escape_path(Util.extract_location(json_file[:parentReference], json_file[:name])),
permissions: %i[readable writeable]
)
end
Expand Down
2 changes: 0 additions & 2 deletions modules/storages/app/models/storages/storage_file_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ module Storages
:size,
:owner_name,
:owner_id,
:trashed,
:last_modified_by_name,
:last_modified_by_id,
:permissions,
Expand All @@ -55,7 +54,6 @@ def initialize(
size: nil,
owner_name: nil,
owner_id: nil,
trashed: nil,
last_modified_by_name: nil,
last_modified_by_id: nil,
permissions: nil,
Expand Down
Loading

0 comments on commit 2d0993f

Please sign in to comment.