Improve logging when validation fails

Add guid and author to error messages if available.
This commit is contained in:
Benjamin Neff 2017-06-11 15:50:01 +02:00
parent d901ceb500
commit c0ea38d258
No known key found for this signature in database
GPG key ID: 971464C3F1A90194
6 changed files with 113 additions and 21 deletions

View file

@ -187,15 +187,21 @@ module DiasporaFederation
type = data.fetch(:parent_type) { type = data.fetch(:parent_type) {
break self::PARENT_TYPE if const_defined?(:PARENT_TYPE) break self::PARENT_TYPE if const_defined?(:PARENT_TYPE)
raise DiasporaFederation::Entity::ValidationError, "invalid #{self}! missing 'parent_type'." raise DiasporaFederation::Entity::ValidationError, error_message_missing_property(data, "parent_type")
} }
guid = data.fetch(:parent_guid) { guid = data.fetch(:parent_guid) {
raise DiasporaFederation::Entity::ValidationError, "invalid #{self}! missing 'parent_guid'." raise DiasporaFederation::Entity::ValidationError, error_message_missing_property(data, "parent_guid")
} }
data[:parent] = RelatedEntity.fetch(data[:author], type, guid) data[:parent] = RelatedEntity.fetch(data[:author], type, guid)
end end
def error_message_missing_property(data, missing_property)
obj_str = "#{class_name}#{":#{data[:guid]}" if data.has_key?(:guid)}" \
"#{" from #{data[:author]}" if data.has_key?(:author)}"
"Invalid #{obj_str}! Missing '#{missing_property}'."
end
def xml_parser_class def xml_parser_class
DiasporaFederation::Parsers::RelayableXmlParser DiasporaFederation::Parsers::RelayableXmlParser
end end

View file

@ -131,7 +131,7 @@ module DiasporaFederation
# #
# @return [String] entity name # @return [String] entity name
def self.entity_name def self.entity_name
name.rpartition("::").last.tap do |word| class_name.tap do |word|
word.gsub!(/(.)([A-Z])/, '\1_\2') word.gsub!(/(.)([A-Z])/, '\1_\2')
word.downcase! word.downcase!
end end
@ -154,9 +154,14 @@ module DiasporaFederation
Entities.const_get(class_name) Entities.const_get(class_name)
end end
# @return [String] class name as string
def self.class_name
name.rpartition("::").last
end
# @return [String] string representation of this object # @return [String] string representation of this object
def to_s def to_s
"#{self.class.name.rpartition('::').last}#{":#{guid}" if respond_to?(:guid)}" "#{self.class.class_name}#{":#{guid}" if respond_to?(:guid)}"
end end
# Renders entity to a hash representation of the entity JSON format # Renders entity to a hash representation of the entity JSON format
@ -190,7 +195,11 @@ module DiasporaFederation
def validate_missing_props(entity_data) def validate_missing_props(entity_data)
missing_props = self.class.missing_props(entity_data) missing_props = self.class.missing_props(entity_data)
raise ValidationError, "missing required properties: #{missing_props.join(', ')}" unless missing_props.empty? return if missing_props.empty?
obj_str = "#{self.class.class_name}#{":#{entity_data[:guid]}" if entity_data.has_key?(:guid)}" \
"#{" from #{entity_data[:author]}" if entity_data.has_key?(:author)}"
raise ValidationError, "#{obj_str}: Missing required properties: #{missing_props.join(', ')}"
end end
def setable?(name, val) def setable?(name, val)
@ -246,7 +255,7 @@ module DiasporaFederation
errors = validator.errors.map do |prop, rule| errors = validator.errors.map do |prop, rule|
"property: #{prop}, value: #{public_send(prop).inspect}, rule: #{rule[:rule]}, with params: #{rule[:params]}" "property: #{prop}, value: #{public_send(prop).inspect}, rule: #{rule[:rule]}, with params: #{rule[:params]}"
end end
"Failed validation for properties: #{errors.join(' | ')}" "Failed validation for #{self}#{" from #{author}" if respond_to?(:author)} for properties: #{errors.join(' | ')}"
end end
# @return [Hash] hash with all properties # @return [Hash] hash with all properties

View file

@ -57,6 +57,12 @@ module DiasporaFederation
entity :multi, [OtherEntity] entity :multi, [OtherEntity]
end end
class TestEntityWithAuthorAndGuid < DiasporaFederation::Entity
property :test, :string
property :author, :string
property :guid, :string
end
class SomeRelayable < DiasporaFederation::Entity class SomeRelayable < DiasporaFederation::Entity
PARENT_TYPE = "Parent".freeze PARENT_TYPE = "Parent".freeze
@ -74,5 +80,13 @@ module DiasporaFederation
rule :test2, :not_nil rule :test2, :not_nil
rule :test3, :boolean rule :test3, :boolean
end end
class TestEntityWithAuthorAndGuidValidator < Validation::Validator
include Validation
rule :test, :boolean
rule :author, %i(not_empty diaspora_id)
rule :guid, :guid
end
end end
end end

View file

@ -62,28 +62,24 @@ JSON
broken_xml = <<-XML broken_xml = <<-XML
<like> <like>
<parent_guid>#{parent.guid}</parent_guid> <parent_guid>#{parent.guid}</parent_guid>
<author_signature>#{data[:author_signature]}</author_signature>
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
</like> </like>
XML XML
expect { expect {
DiasporaFederation::Entities::Like.from_xml(Nokogiri::XML(broken_xml).root) DiasporaFederation::Entities::Like.from_xml(Nokogiri::XML(broken_xml).root)
}.to raise_error Entity::ValidationError, "invalid DiasporaFederation::Entities::Like! missing 'parent_type'." }.to raise_error Entity::ValidationError, "Invalid Like! Missing 'parent_type'."
end end
it "raises a ValidationError if the parent_guid is missing" do it "raises a ValidationError if the parent_guid is missing" do
broken_xml = <<-XML broken_xml = <<-XML
<like> <like>
<target_type>#{parent.entity_type}</target_type> <target_type>#{parent.entity_type}</target_type>
<author_signature>#{data[:author_signature]}</author_signature>
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
</like> </like>
XML XML
expect { expect {
DiasporaFederation::Entities::Like.from_xml(Nokogiri::XML(broken_xml).root) DiasporaFederation::Entities::Like.from_xml(Nokogiri::XML(broken_xml).root)
}.to raise_error Entity::ValidationError, "invalid DiasporaFederation::Entities::Like! missing 'parent_guid'." }.to raise_error Entity::ValidationError, "Invalid Like! Missing 'parent_guid'."
end end
end end
end end

View file

@ -256,15 +256,36 @@ XML
it "raises a ValidationError if the parent_guid is missing" do it "raises a ValidationError if the parent_guid is missing" do
broken_xml = <<-XML broken_xml = <<-XML
<some_relayable> <some_relayable>
<author_signature/>
<parent_author_signature/>
</some_relayable> </some_relayable>
XML XML
expect { expect {
Entities::SomeRelayable.from_xml(Nokogiri::XML(broken_xml).root) Entities::SomeRelayable.from_xml(Nokogiri::XML(broken_xml).root)
}.to raise_error Entity::ValidationError, }.to raise_error Entity::ValidationError, "Invalid SomeRelayable! Missing 'parent_guid'."
"invalid DiasporaFederation::Entities::SomeRelayable! missing 'parent_guid'." end
it "adds the guid to the error message if available" do
broken_xml = <<-XML
<some_relayable>
<guid>#{guid}</guid>
</some_relayable>
XML
expect {
Entities::SomeRelayable.from_xml(Nokogiri::XML(broken_xml).root)
}.to raise_error Entity::ValidationError, "Invalid SomeRelayable:#{guid}! Missing 'parent_guid'."
end
it "adds the author to the error message if available" do
broken_xml = <<-XML
<some_relayable>
<author>#{author}</author>
</some_relayable>
XML
expect {
Entities::SomeRelayable.from_xml(Nokogiri::XML(broken_xml).root)
}.to raise_error Entity::ValidationError, "Invalid SomeRelayable from #{author}! Missing 'parent_guid'."
end end
end end
end end

View file

@ -1,6 +1,7 @@
module DiasporaFederation module DiasporaFederation
describe Entity do describe Entity do
let(:data) { {test1: "asdf", test2: 1234, test3: false, test4: false} } let(:data) { {test1: "asdf", test2: 1234, test3: false, test4: false} }
let(:guid) { Fabricate.sequence(:guid) }
it "should extend Entity" do it "should extend Entity" do
expect(Entities::TestDefaultEntity).to be < Entity expect(Entities::TestDefaultEntity).to be < Entity
@ -12,10 +13,26 @@ module DiasporaFederation
expect(entity).to be_frozen expect(entity).to be_frozen
end end
it "checks for required properties" do context "required properties" do
expect { it "checks for required properties" do
Entities::TestDefaultEntity.new({}) expect {
}.to raise_error Entity::ValidationError, "missing required properties: test1, test2" Entities::TestDefaultEntity.new({})
}.to raise_error Entity::ValidationError, "TestDefaultEntity: Missing required properties: test1, test2"
end
it "adds the guid to the error message if available" do
expect {
Entities::TestDefaultEntity.new(guid: guid)
}.to raise_error Entity::ValidationError,
"TestDefaultEntity:#{guid}: Missing required properties: test1, test2"
end
it "adds the author to the error message if available" do
expect {
Entities::TestDefaultEntity.new(author: alice.diaspora_id)
}.to raise_error Entity::ValidationError,
"TestDefaultEntity from #{alice.diaspora_id}: Missing required properties: test1, test2"
end
end end
context "defaults" do context "defaults" do
@ -48,7 +65,8 @@ module DiasporaFederation
it "validates the entity and raise an error with failed properties if not valid" do it "validates the entity and raise an error with failed properties if not valid" do
expect { expect {
Entities::TestDefaultEntity.new(invalid_data) Entities::TestDefaultEntity.new(invalid_data)
}.to raise_error Entity::ValidationError, /Failed validation for properties:.*test1.*\|.*test2.*\|.*test3/ }.to raise_error Entity::ValidationError,
/Failed validation for TestDefaultEntity for properties:.*test1.*\|.*test2.*\|.*test3/
end end
it "contains the failed rule" do it "contains the failed rule" do
@ -62,6 +80,34 @@ module DiasporaFederation
Entities::TestDefaultEntity.new(invalid_data) Entities::TestDefaultEntity.new(invalid_data)
}.to raise_error Entity::ValidationError, /rule: regular_expression, with params: \{:regex=>.*\}/ }.to raise_error Entity::ValidationError, /rule: regular_expression, with params: \{:regex=>.*\}/
end end
it "adds the guid to the error message if available" do
expect {
Entities::TestEntityWithAuthorAndGuid.new(test: "invalid", guid: guid, author: alice.diaspora_id)
}.to raise_error Entity::ValidationError,
/Failed validation for TestEntityWithAuthorAndGuid:#{guid} from .* for properties:.*/
end
it "handles missing guid" do
expect {
Entities::TestEntityWithAuthorAndGuid.new(test: "invalid", guid: nil, author: alice.diaspora_id)
}.to raise_error Entity::ValidationError,
/Failed validation for TestEntityWithAuthorAndGuid: from .* for properties:.*/
end
it "adds the author to the error message if available" do
expect {
Entities::TestEntityWithAuthorAndGuid.new(test: "invalid", guid: guid, author: alice.diaspora_id)
}.to raise_error Entity::ValidationError,
/Failed validation for .* from #{alice.diaspora_id} for properties:.*/
end
it "handles missing author" do
expect {
Entities::TestEntityWithAuthorAndGuid.new(test: "invalid", guid: guid, author: nil)
}.to raise_error Entity::ValidationError,
/Failed validation for .* from for properties:.*/
end
end end
end end