Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] dev from opf:dev #516

Merged
merged 6 commits into from
Mar 28, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/models/types/patterns/token_property_mapper.rb
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ class TokenPropertyMapper
creation_date: { fn: ->(wp) { wp.created_at }, label: -> { WorkPackage.human_attribute_name(:created_at) } },
estimated_time: { fn: ->(wp) { wp.estimated_hours }, label: -> { WorkPackage.human_attribute_name(:estimated_hours) } },
finish_date: { fn: ->(wp) { wp.due_date }, label: -> { WorkPackage.human_attribute_name(:due_date) } },
parent_id: { fn: ->(wp) { wp.parent&.id }, label: -> { WorkPackage.human_attribute_name(:parent_id) } },
parent_id: { fn: ->(wp) { wp.parent&.id }, label: -> { WorkPackage.human_attribute_name(:id) } },
parent_assignee: { fn: ->(wp) { wp.parent&.assigned_to&.name }, label: -> {
WorkPackage.human_attribute_name(:assigned_to)
} },
6 changes: 1 addition & 5 deletions app/models/work_package/pdf_export/export/wp/attributes.rb
Original file line number Diff line number Diff line change
@@ -46,14 +46,10 @@ def write_attributes!(work_package)
def write_long_text_field!(work_package, field_id)
custom_value = work_package.custom_field_values
.find { |cv| cv.custom_field.id == field_id && cv.custom_field.formattable? }
if custom_value&.value
write_long_text_custom_field!(work_package, custom_value.value, custom_value.custom_field.name)
end
write_long_text_custom_field!(work_package, custom_value.value || "", custom_value.custom_field.name)
end

def write_long_text_custom_field!(work_package, markdown, label)
return if markdown.blank?

write_optional_page_break
write_long_text_custom_field_label(label)
write_markdown_field_value(work_package, markdown)
2 changes: 1 addition & 1 deletion lib/api/v3/versions/versions_api.rb
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ class VersionsAPI < ::API::OpenProjectAPI
# the distinct(false) is added in order to allow ORDER BY LOWER(name)
# which would otherwise be invalid in postgresql
# SELECT DISTINCT, ORDER BY expressions must appear in select list
Version.visible(current_user).distinct(false)
Version.visible(current_user).or(Version.systemwide).distinct(false)
})
.mount

Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# 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 ConnectionValidators
class BaseValidatorGroup
include TaggedLogging

def initialize(storage)
@storage = storage
@results = ValidationGroupResult.new
end

def call
catch :interrupted do
validate
end

@results
end

private

def validate = raise Errors::SubclassResponsibility

def register_checks(*keys)
keys.each { @results.register_check(it) }
end

def update_result(...)
@results.update_result(...)
end

def pass_check(key)
update_result(key, CheckResult.success(key))
end

def fail_check(key, message)
update_result(key, CheckResult.failure(key, message))
throw :interrupted
end

def warn_check(key, message, halt_validation: false)
update_result(key, CheckResult.warning(key, message))
throw :interrupted if halt_validation
end

def message(key, context = {})
I18n.t("storages.health.connection_validation.#{key}", **context)
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -32,19 +32,19 @@ module Storages
module Peripherals
module ConnectionValidators
module Nextcloud
class AmpfConnectionValidator < BaseValidator
class AmpfConfigurationValidator < BaseValidatorGroup
using ServiceResultRefinements

private

def validate
register_checks(
:userless_access, :group_folder_presence, :files_request, :group_folder_contents
:files_request, :userless_access, :group_folder_presence, :group_folder_contents
)

files_request_failed_with_unknown_error
userless_access_denied
group_folder_not_found
files_request_failed_with_unknown_error
with_unexpected_content
end

Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ module Storages
module Peripherals
module ConnectionValidators
module Nextcloud
class AuthenticationValidator < BaseValidator
class AuthenticationValidator < BaseValidatorGroup
def initialize(storage)
super
@user = User.current
Original file line number Diff line number Diff line change
@@ -32,21 +32,31 @@ module Storages
module Peripherals
module ConnectionValidators
module Nextcloud
class BaseConfigurationValidator < BaseValidator
class StorageConfigurationValidator < BaseValidatorGroup
private

def validate
register_checks(:capabilities_request, :host_url_accessible, :dependencies_check, :dependencies_versions)
register_checks(:storage_configured, :capabilities_request,
:host_url_accessible, :dependencies_check, :dependencies_versions)

storage_configuration_status
capabilities_request_status
host_url_not_found
missing_dependencies
version_mismatch
end

def storage_configuration_status
if @storage.configured?
pass_check(:storage_configured)
else
fail_check(:storage_configured, message(:not_configured))
end
end

def capabilities_request_status
if capabilities.failure? && capabilities.result != :not_found
fail_check(:capabilities_request, message(:error))
fail_check(:capabilities_request, message(:unknown_error))
else
pass_check(:capabilities_request)
end
Original file line number Diff line number Diff line change
@@ -31,56 +31,41 @@
module Storages
module Peripherals
module ConnectionValidators
module Nextcloud
class BaseValidator
include TaggedLogging

def initialize(storage)
@storage = storage
class NextcloudValidator
# Class Level interface will be moved to a superclass/mixin once we do the OneDrive port
class << self
def validation_groups
@validation_groups ||= {}
end

def call
catch :interrupted do
validate
end

@results
def register_group(group_name, klass, when: ->(*) { true })
validation_groups[group_name] = { klass:, when: }
end
end

private

def validate = raise Errors::SubclassResponsibility
register_group :base_configuration, Nextcloud::StorageConfigurationValidator
register_group :authentication, Nextcloud::AuthenticationValidator,
when: ->(_, result) { result.group(:base_configuration).non_failure? }
register_group :ampf_configuration, Nextcloud::AmpfConfigurationValidator,
when: ->(storage, result) {
result.group(:base_configuration).non_failure? && storage.automatic_management_enabled?
}

def register_checks(*keys)
@results = keys.to_h { [it, CheckResult.skipped(it)] }
end
def initialize(storage:)
@storage = storage
end

def update_result(key, value)
if @results&.has_key?(key)
@results[key] = value
else
raise ArgumentError, "Check #{key} not registered."
def validate
validation_groups.each_with_object(ValidatorResult.new) do |(key, group_metadata), result|
if group_metadata[:when].call(@storage, result)
result.add_group_result(key, group_metadata[:klass].new(@storage).call)
end
end
end

def pass_check(key)
update_result(key, CheckResult.success(key))
end

def fail_check(key, message)
update_result(key, CheckResult.failure(key, message))
throw :interrupted
end
private

def warn_check(key, message, halt_validation: false)
update_result(key, CheckResult.warning(key, message))
throw :interrupted if halt_validation
end

def message(key, context = {})
I18n.t("storages.health.connection_validation.#{key}", **context)
end
end
def validation_groups = self.class.validation_groups
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# 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 ConnectionValidators
class ValidationGroupResult
delegate :[], to: :@results

def initialize
@results = {}
end

def success? = @results.values.all?(&:success?)
def non_failure? = @results.values.none?(&:failure?)
def failure? = @results.values.any?(&:failure?)
def warning? = @results.values.any?(&:warning?)

def tally
@results.values.map(&:state).tally
end

def register_checks(keys)
Array(keys).each { register_check(it) }
end

def register_check(key)
warn("Overriding already defined check") if @results.key?(key)

@results[key] = CheckResult.skipped(key)
end

def update_result(key, value)
raise(ArgumentError, "Check #{key} not registered.") unless @results.key?(key)

@results[key] = value
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# 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 ConnectionValidators
class ValidatorResult
private attr_reader :group_results

delegate :each_pair, :empty?, to: :group_results

def initialize
@group_results = {}
end

def healthy? = group_results.values.all?(&:success?)
def unhealthy? = group_results.values.any?(&:failure?)
def warning? = group_results.values.any?(&:warning?)

def group(key) = group_results.fetch(key)
alias_method :fetch, :group

def add_group_result(key, result)
Kernel.warn "Overwriting #{key} results" if group_results.key?(key)

group_results[key] = result
end
end
end
end
end
Loading
Loading