-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feature/validation' into dev
- Loading branch information
Showing
25 changed files
with
780 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# frozen_string_literal: true | ||
|
||
# rubocop:disable Lint/UnusedMethodArgument | ||
|
||
require_relative 'interface' | ||
require_relative '../mixin/errors' | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module API | ||
# Custom validator for attribute | ||
class AttributeValidator < Interface | ||
include Mixin::Errors | ||
# :nocov: | ||
# @param [Object] value Attribute value | ||
# @param [Mapper::Type::Attribute] attribute | ||
# @param [Mapper::Context] context | ||
# @return [Array<String>] List of violations | ||
def validate(value, attribute, context) | ||
abstract_method | ||
end | ||
# :nocov: | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative '../attribute_validator' | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module API | ||
module Default | ||
# Default validator for single attribute | ||
class AttributeValidator < API::AttributeValidator | ||
INSTANCE = new | ||
|
||
# @param [Object] value Attribute value | ||
# @param [AMA::Entity::Mapper::Type::Attribute] attribute | ||
# @return [Array<String>] Single violation, list of violations | ||
def validate(value, attribute, *) | ||
violations = validate_internal(value, attribute) | ||
violations.nil? ? [] : [violations] | ||
end | ||
|
||
private | ||
|
||
def validate_internal(value, attribute) | ||
if illegal_nil?(value, attribute) | ||
return "Attribute #{attribute} could not be nil" | ||
end | ||
if invalid_type?(value, attribute) | ||
return "Provided value #{value} doesn't conform to " \ | ||
"any of attribute #{attribute} types (#{attribute.types})" | ||
end | ||
return unless illegal_value?(value, attribute) | ||
"Provided value #{value} doesn't match default value (#{value})" \ | ||
" or any of allowed values (#{attribute.values})" | ||
end | ||
|
||
# @param [Object] value Attribute value | ||
# @param [AMA::Entity::Mapper::Type::Attribute] attribute | ||
# @return [TrueClass, FalseClass] | ||
def illegal_nil?(value, attribute) | ||
value.nil? && !attribute.nullable | ||
end | ||
|
||
# @param [Object] value Attribute value | ||
# @param [AMA::Entity::Mapper::Type::Attribute] attribute | ||
# @return [TrueClass, FalseClass] | ||
def invalid_type?(value, attribute) | ||
attribute.types.all? do |type| | ||
!type.respond_to?(:instance?) || !type.instance?(value) | ||
end | ||
end | ||
|
||
# @param [Object] value Attribute value | ||
# @param [AMA::Entity::Mapper::Type::Attribute] attribute | ||
# @return [TrueClass, FalseClass] | ||
def illegal_value?(value, attribute) | ||
return false if value == attribute.default | ||
return false if attribute.values.empty? || attribute.values.nil? | ||
!attribute.values.include?(value) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative '../entity_validator' | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module API | ||
module Default | ||
# Default entity validation | ||
class EntityValidator < API::EntityValidator | ||
INSTANCE = new | ||
|
||
# @param [Object] entity | ||
# @param [Mapper::Type::Concrete] type | ||
# @param [Mapper::Context] context | ||
# @return [Array<Array<Attribute, String, Segment>] List of | ||
# violations, combined with attribute and segment | ||
def validate(entity, type, context) | ||
enumerator = type.enumerator.enumerate(entity, type, context) | ||
enumerator.flat_map do |attribute, value, segment = nil| | ||
next_context = segment.nil? ? context : context.advance(segment) | ||
validator = attribute.validator | ||
violations = validator.validate(value, attribute, next_context) | ||
violations.map do |violation| | ||
[attribute, violation, segment] | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# frozen_string_literal: true | ||
|
||
# rubocop:disable Lint/UnusedMethodArgument | ||
|
||
require_relative 'interface' | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module API | ||
# Custom validator for entity | ||
class EntityValidator < Interface | ||
# :nocov: | ||
# @param [Object] entity | ||
# @param [Mapper::Type::Concrete] type | ||
# @param [Mapper::Context] context | ||
# @return [Array<Array<Attribute, String, Segment>] List of | ||
# violations, combined with attribute and segment | ||
def validate(entity, type, context) | ||
abstract_method | ||
end | ||
# :nocov: | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative '../attribute_validator' | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module API | ||
module Wrapper | ||
# Attribute validator safety wrapper | ||
class AttributeValidator < API::AttributeValidator | ||
# @param [AMA::Entity::Mapper::API::AttributeValidator] validator | ||
def initialize(validator) | ||
@validator = validator | ||
end | ||
|
||
# @param [Object] value Attribute value | ||
# @param [AMA::Entity::Mapper::Type::Attribute] attr | ||
# @param [AMA::Entity::Mapper::Context] ctx | ||
# @return [Array<String>] List of violations | ||
def validate(value, attr, ctx) | ||
violations = @validator.validate(value, attr, ctx) do |v, a, c| | ||
API::Default::AttributeValidator::INSTANCE.validate(v, a, c) | ||
end | ||
violations = [violations] if violations.is_a?(String) | ||
violations.nil? ? [] : violations | ||
rescue StandardError => e | ||
raise_if_internal(e) | ||
message = "Error during #{attr} validation (value: #{value})" | ||
if e.is_a?(ArgumentError) | ||
message += '. Does provided validator have ' \ | ||
'(value, attribute, context) signature?' | ||
end | ||
compliance_error(message, context: ctx, parent: e) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative '../entity_validator' | ||
require_relative '../../type/attribute' | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module API | ||
module Wrapper | ||
# Default entity validation | ||
class EntityValidator < API::EntityValidator | ||
# @param [AMA::Entity::Mapper::API::EntityValidator] validator | ||
def initialize(validator) | ||
@validator = validator | ||
end | ||
|
||
# @param [Object] entity | ||
# @param [Mapper::Type::Concrete] type | ||
# @param [Mapper::Context] context | ||
# @return [Array<Array<Attribute, String, Segment>] List of | ||
# violations, combined with attribute and segment | ||
def validate(entity, type, context) | ||
result = @validator.validate(entity, type, context) do |e, t, c| | ||
API::Default::EntityValidator::INSTANCE.validate(e, t, c) | ||
end | ||
verify_result!(result, type, context) | ||
result | ||
rescue StandardError => e | ||
raise_if_internal(e) | ||
message = "Error during #{type} validation (entity: #{entity})" | ||
if e.is_a?(ArgumentError) | ||
message += '. Does provided validator have ' \ | ||
'(entity, type, context) signature?' | ||
end | ||
compliance_error(message, context: context, parent: e) | ||
end | ||
|
||
private | ||
|
||
def verify_result!(result, type, context) | ||
unless result.is_a?(Array) | ||
message = "Validator #{@validator} for type #{type} " \ | ||
'had to return list of violations, ' \ | ||
"#{result} was received instead" | ||
compliance_error(message, context: context) | ||
end | ||
result.each do |violation| | ||
verify_violation!(violation, type, context) | ||
end | ||
end | ||
|
||
def verify_violation!(violation, type, context) | ||
message = "Validator #{@validator} for type #{type} " \ | ||
"has returned #{violation} as violation " \ | ||
'([attribute, violation, path segment] expected)' | ||
unless violation.is_a?(Array) || violation.size == 3 | ||
compliance_error(message, context: context) | ||
end | ||
conditions = [ | ||
violation[0].is_a?(Type::Attribute), | ||
violation[1].is_a?(String), | ||
violation[2].is_a?(Path::Segment) || violation[2].nil? | ||
] | ||
return if conditions.all?(&:itself) | ||
compliance_error(message, context: context) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.