Skip to content

Commit

Permalink
Fixes zammad#5276 - Untranslated error message in /object_manager
Browse files Browse the repository at this point in the history
  • Loading branch information
mantas committed Jul 30, 2024
1 parent 73a3042 commit e2f75d1
Show file tree
Hide file tree
Showing 7 changed files with 526 additions and 261 deletions.
2 changes: 2 additions & 0 deletions app/controllers/object_manager_attributes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ def add_attribute_using_params(given_params, status:)
object_manager_attribute = ObjectManager::Attribute.add(attributes)

render json: object_manager_attribute.attributes_with_association_ids, status: status
rescue ActiveRecord::RecordInvalid => e
raise e
rescue => e
logger.error e
raise Exceptions::UnprocessableEntity, e
Expand Down
91 changes: 10 additions & 81 deletions app/models/object_manager/attribute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,18 @@ class ObjectManager::Attribute < ApplicationModel
active
].freeze

VALIDATE_INTEGER_MIN = -2_147_483_647
VALIDATE_INTEGER_MAX = 2_147_483_647
VALIDATE_INTEGER_REGEXP = %r{^-?\d+$}

self.table_name = 'object_manager_attributes'

belongs_to :object_lookup, optional: true

validates :name, presence: true
validates :data_type, inclusion: { in: DATA_TYPES, msg: '%{value} is not a valid data type' }
validate :inactive_must_be_unused_by_references, unless: :active?
validate :data_option_must_have_appropriate_values
validate :data_type_must_not_change, on: :update
validate :json_field_only_on_postgresql, on: :create

validates_with ObjectManager::Attribute::DataOptionValidator

store :screens
store :data_option
store :data_option_new
Expand Down Expand Up @@ -943,6 +940,14 @@ def check_editable
raise ActiveRecord::RecordInvalid, self
end

def local_data_option
send(local_data_attr)
end

def local_data_option=(val)
send(:"#{local_data_attr}=", val)
end

private

# when setting default values for boolean fields,
Expand All @@ -959,12 +964,6 @@ def set_base_options
end
end

def data_option_must_have_appropriate_values
data_option_validations
.select { |validation| validation[:failed] }
.each { |validation| errors.add(local_data_attr, validation[:message]) }
end

def inactive_must_be_unused_by_references
return if !ObjectManager::Attribute.attribute_used_by_references?(object_lookup.name, name)

Expand All @@ -991,80 +990,10 @@ def json_field_only_on_postgresql
errors.add(:data_type, __('can only be created on postgresql databases'))
end

def local_data_option
@local_data_option ||= send(local_data_attr)
end

def local_data_attr
@local_data_attr ||= to_config ? :data_option_new : :data_option
end

def local_data_option=(val)
send(:"#{local_data_attr}=", val)
end

def data_option_maxlength_check
[{ failed: !local_data_option[:maxlength].to_s.match?(%r{^\d+$}), message: 'must have integer for :maxlength' }]
end

def data_option_type_check
[{ failed: %w[text password tel fax email url].exclude?(local_data_option[:type]), message: 'must have one of text/password/tel/fax/email/url for :type' }]
end

def data_option_min_max_check
min = local_data_option[:min]
max = local_data_option[:max]

[
{ failed: !VALIDATE_INTEGER_REGEXP.match?(min.to_s), message: 'must have integer for :min' },
{ failed: !VALIDATE_INTEGER_REGEXP.match?(max.to_s), message: 'must have integer for :max' },
{ failed: !(min.is_a?(Integer) && min >= VALIDATE_INTEGER_MIN), message: 'min must be higher than -2147483648' },
{ failed: !(min.is_a?(Integer) && min <= VALIDATE_INTEGER_MAX), message: 'min must be lower than 2147483648' },
{ failed: !(max.is_a?(Integer) && max >= VALIDATE_INTEGER_MIN), message: 'max must be higher than -2147483648' },
{ failed: !(max.is_a?(Integer) && max <= VALIDATE_INTEGER_MAX), message: 'max must be lower than 2147483648' },
{ failed: !(max.is_a?(Integer) && min.is_a?(Integer) && min <= max), message: 'min must be lower than max' }
]
end

def data_option_default_check
[{ failed: !local_data_option.key?(:default), message: 'must have value for :default' }]
end

def data_option_relation_check
[{ failed: local_data_option[:options].nil? && local_data_option[:relation].nil?, message: 'must have non-nil value for either :options or :relation' }]
end

def data_option_nil_check
[{ failed: local_data_option[:options].nil?, message: 'must have non-nil value for :options' }]
end

def data_option_future_check
[{ failed: local_data_option[:future].nil?, message: 'must have boolean value for :future' }]
end

def data_option_past_check
[{ failed: local_data_option[:past].nil?, message: 'must have boolean value for :past' }]
end

def data_option_validations
case data_type
when 'input'
data_option_type_check + data_option_maxlength_check
when %r{^(textarea|richtext)$}
data_option_maxlength_check
when 'integer'
data_option_min_max_check
when %r{^((multi_)?tree_select|(multi)?select|checkbox)$}
data_option_default_check + data_option_relation_check
when 'boolean'
data_option_default_check + data_option_nil_check
when 'datetime'
data_option_future_check + data_option_past_check
else
[]
end
end

def ensure_multiselect
return if data_type != 'multiselect' && data_type != 'multi_tree_select'
return if data_option && data_option[:multiple] == true
Expand Down
134 changes: 134 additions & 0 deletions app/models/object_manager/attribute/data_option_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/

class ObjectManager::Attribute::DataOptionValidator < ActiveModel::Validator
VALIDATE_INTEGER_MIN = -2_147_483_647
VALIDATE_INTEGER_MAX = 2_147_483_647
VALIDATE_UNSIGNED_INTEGER_REGEXP = %r{^\d+$}
INPUT_DATA_TYPES = %w[text password tel fax email url].freeze

def validate(record)
case record.data_type
when 'input'
type_check(record)
maxlength_check(record)
when %r{^(textarea|richtext)$}
maxlength_check(record)
when 'integer'
min_max_check(record)
when %r{^((multi_)?tree_select|(multi)?select|checkbox)$}
default_check(record)
relation_check(record)
when 'boolean'
default_check(record)
presence_check(record)
when 'datetime'
future_check(record)
past_check(record)
end
end

private

def maxlength_check(record)
return if VALIDATE_UNSIGNED_INTEGER_REGEXP.match?(record.local_data_option[:maxlength].to_s)

record.errors.add :base, __('Max length must be an integer.')
end

def type_check(record)
return if INPUT_DATA_TYPES.include? record.local_data_option[:type]

record.errors.add :base, __('Input field must be text, password, tel, fax, email or url type.')
end

def default_check(record)
return if record.local_data_option.key?(:default)

record.errors.add :base, __('Default value is required.')
end

def relation_check(record)
return if !record.local_data_option[:options].nil? || !record.local_data_option[:relation].nil?

record.errors.add :base, __('Options or relation is required.')
end

def presence_check(record)
return if !record.local_data_option[:options].nil?

record.errors.add :base, __('Options are required.')
end

def future_check(record)
return if !record.local_data_option[:future].nil?

record.errors.add :base, __('Allow future dates toggle value is required.')
end

def past_check(record)
return if !record.local_data_option[:past].nil?

record.errors.add :base, __('Allow past dates toggle value is required.')
end

def min_max_check(record)
min_ok = min_max_validate_min(record)
max_ok = min_max_validate_max(record)

return if !min_ok || !max_ok

min_max_validate_range(record)
end

def min_max_validate_min(record)
min = record.local_data_option[:min]

if !min.is_a?(Integer)
record.errors.add :base, __('Minimal value must be an integer')

return
end

if min < VALIDATE_INTEGER_MIN
record.errors.add :base, __('Minimal value must be higher than -2147483648')
return
end

if min > VALIDATE_INTEGER_MAX
record.errors.add :base, __('Minimal value must be lower than 2147483648')
return
end

true
end

def min_max_validate_max(record)
max = record.local_data_option[:max]

if !max.is_a?(Integer)
record.errors.add :base, __('Maximal value must be an integer')
return
end

if max < VALIDATE_INTEGER_MIN
record.errors.add :base, __('Maximal value must be higher than -2147483648')
return
end

if max > VALIDATE_INTEGER_MAX
record.errors.add :base, __('Maximal value must be lower than 2147483648')
return
end

true
end

def min_max_validate_range(record)
min = record.local_data_option[:min]
max = record.local_data_option[:max]

return if min.is_a?(Integer) && max.is_a?(Integer) && min <= max

record.errors.add :base, __('Maximal value must be higher than or equal to minimal value')
end
end
Loading

0 comments on commit e2f75d1

Please sign in to comment.