diff --git a/leia-common/src/main/java/com/grookage/leia/common/utils/SchemaValidationUtils.java b/leia-common/src/main/java/com/grookage/leia/common/utils/SchemaValidationUtils.java index 44339d9..8a1fa4c 100644 --- a/leia-common/src/main/java/com/grookage/leia/common/utils/SchemaValidationUtils.java +++ b/leia-common/src/main/java/com/grookage/leia/common/utils/SchemaValidationUtils.java @@ -50,18 +50,20 @@ public class SchemaValidationUtils { private static final String TYPE_VIOLATION = "Incompatible Type, expected: %s, provided: %s"; - public Set valid(final SchemaDetails schemaDetails, - final Class klass) { + public List valid(final SchemaDetails schemaDetails, + final Class klass) { return valid(schemaDetails.getValidationType(), schemaDetails.getAttributes(), klass, new ViolationContext()); } - public Set valid(final SchemaValidationType validationType, - final Set attributes, - final Class klass, - final ViolationContext context) { + public List valid(final SchemaValidationType validationType, + final Set attributes, + final Class klass, + final ViolationContext context) { + context.pushClass(klass); final var fields = FieldUtils.getAllFields(klass); validSchema(validationType, attributes, fields, klass, context); attributes.forEach(each -> validAttribute(each, fields, validationType, context)); + context.popClass(); return context.getViolations(); } @@ -84,8 +86,7 @@ private void validSchema(final SchemaValidationType validationType, public Void strict() { final var mismatchedAttributes = Sets.symmetricDifference(fieldNames, attributesListed); if (!mismatchedAttributes.isEmpty()) { - context.addViolation(String.format("%s - [STRICT] Validation: attributes not found or extra attributes :%s", - klass.getSimpleName(), mismatchedAttributes)); + context.addViolation(String.format("[STRICT] Validation: attributes not found or extra attributes :%s", mismatchedAttributes)); } return null; } @@ -94,8 +95,7 @@ public Void strict() { public Void matching() { final var attributesMissing = Sets.difference(attributesListed, fieldNames); if (!attributesMissing.isEmpty()) { - context.addViolation(String.format("%s - [MATCHING] Validation: Missing attributes found :%s", - klass.getSimpleName(), attributesMissing)); + context.addViolation(String.format("[MATCHING] Validation: Missing attributes found :%s", attributesMissing)); } return null; } @@ -109,13 +109,11 @@ private void validAttribute(final SchemaAttribute attribute, final var field = fields.stream() .filter(each -> each.getName().equals(attribute.getName())) .findFirst().orElse(null); - context.pushPath(attribute.getName()); if (field == null) { - context.addViolation("Missing Field"); + context.addViolation("Missing Field", attribute.getName()); return; } valid(validationType, attribute, field.getGenericType(), context); - context.popPath(); } private void valid(final SchemaValidationType validationType, @@ -134,22 +132,24 @@ private void valid(final SchemaValidationType validationType, } private void valid(final SchemaValidationType validationType, - final SchemaAttribute attribute, + final SchemaAttribute schemaAttribute, final Class klass, final ViolationContext context) { - if (!isMatchingType(klass, attribute)) { - context.addViolation(String.format(TYPE_VIOLATION, attribute.getType(), klass.getSimpleName())); + if (!isMatchingType(klass, schemaAttribute)) { + context.addViolation(String.format(TYPE_VIOLATION, schemaAttribute.getType(), klass.getSimpleName()), + schemaAttribute.getName()); return; } - attribute.accept(new SchemaAttributeHandler(attribute1 -> null) { + schemaAttribute.accept(new SchemaAttributeHandler(a -> null) { @Override public Void accept(ArrayAttribute attribute) { if (klass.isArray()) { valid(validationType, attribute.getElementAttribute(), klass.getComponentType(), context); return null; } else if (!Objects.isNull(attribute.getElementAttribute())) { - context.addViolation(String.format("Missing Type arguments, expected ParameterizedType:%s", attribute.getElementAttribute().getType())); + context.addViolation(String.format("Missing Type arguments, expected ParameterizedType:%s", + attribute.getElementAttribute().getType()), attribute.getName()); } return null; } @@ -158,7 +158,7 @@ public Void accept(ArrayAttribute attribute) { public Void accept(MapAttribute attribute) { if (!Objects.isNull(attribute.getKeyAttribute()) || !Objects.isNull(attribute.getValueAttribute())) { context.addViolation(String.format("Missing Type Arguments, expected parameterized Types key:%s value:%s", - attribute.getKeyAttribute().getType(), attribute.getValueAttribute().getType())); + attribute.getKeyAttribute().getType(), attribute.getValueAttribute().getType()), attribute.getName()); } return null; } @@ -189,7 +189,7 @@ private void valid(final SchemaValidationType validationType, valid(validationType, mapAttribute.getKeyAttribute(), typeArguments[0], context); valid(validationType, mapAttribute.getValueAttribute(), typeArguments[1], context); } else { - context.addViolation(String.format(TYPE_VIOLATION, attribute.getType(), parameterizedType)); + context.addViolation(String.format(TYPE_VIOLATION, attribute.getType(), parameterizedType), attribute.getName()); } } @@ -201,7 +201,7 @@ private void valid(final SchemaValidationType validationType, valid(validationType, arrayAttribute.getElementAttribute(), arrayType.getGenericComponentType(), context); return; } - context.addViolation(String.format(TYPE_VIOLATION, attribute.getType(), arrayType)); + context.addViolation(String.format(TYPE_VIOLATION, attribute.getType(), arrayType), attribute.getName()); } private boolean isMatchingType(final Class klass, diff --git a/leia-common/src/main/java/com/grookage/leia/common/violation/LeiaSchemaViolation.java b/leia-common/src/main/java/com/grookage/leia/common/violation/LeiaSchemaViolation.java index 7b09630..532bbd9 100644 --- a/leia-common/src/main/java/com/grookage/leia/common/violation/LeiaSchemaViolation.java +++ b/leia-common/src/main/java/com/grookage/leia/common/violation/LeiaSchemaViolation.java @@ -4,4 +4,6 @@ public interface LeiaSchemaViolation { String message(); String fieldPath(); + + Class rootKlass(); } diff --git a/leia-common/src/main/java/com/grookage/leia/common/violation/LeiaSchemaViolationImpl.java b/leia-common/src/main/java/com/grookage/leia/common/violation/LeiaSchemaViolationImpl.java index 2741fed..e75f9de 100644 --- a/leia-common/src/main/java/com/grookage/leia/common/violation/LeiaSchemaViolationImpl.java +++ b/leia-common/src/main/java/com/grookage/leia/common/violation/LeiaSchemaViolationImpl.java @@ -1,5 +1,6 @@ package com.grookage.leia.common.violation; +import com.google.common.base.Joiner; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -12,6 +13,7 @@ public class LeiaSchemaViolationImpl implements LeiaSchemaViolation { private String message; private String fieldPath; + private Class rootKlass; @Override public String message() { @@ -24,7 +26,13 @@ public String fieldPath() { } @Override + public Class rootKlass() { + return rootKlass; + } + public String toString() { - return String.format("[Violation] %s: %s", fieldPath, message); + return Joiner.on(":") + .skipNulls() + .join(rootKlass, fieldPath, message); } } diff --git a/leia-common/src/main/java/com/grookage/leia/common/violation/ViolationContext.java b/leia-common/src/main/java/com/grookage/leia/common/violation/ViolationContext.java index 92e430e..f6e2910 100644 --- a/leia-common/src/main/java/com/grookage/leia/common/violation/ViolationContext.java +++ b/leia-common/src/main/java/com/grookage/leia/common/violation/ViolationContext.java @@ -5,30 +5,34 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import java.util.HashSet; +import java.util.ArrayList; import java.util.LinkedList; -import java.util.Set; +import java.util.List; @Data @Builder @NoArgsConstructor public class ViolationContext { @Getter - private final Set violations = new HashSet<>(); - private final LinkedList path = new LinkedList<>(); + private final List violations = new ArrayList<>(); + private final LinkedList> klassPath = new LinkedList<>(); public void addViolation(final String message) { - String fullPath = String.join(".", path); - violations.add(new LeiaSchemaViolationImpl(message, fullPath)); + violations.add(new LeiaSchemaViolationImpl(message, null, klassPath.peekLast())); } - public void pushPath(final String element) { - path.addLast(element); + public void addViolation(final String message, + final String path) { + violations.add(new LeiaSchemaViolationImpl(message, path, klassPath.peekLast())); } - public void popPath() { - if (!path.isEmpty()) { - path.removeLast(); + public void pushClass(final Class klass) { + klassPath.addLast(klass); + } + + public void popClass() { + if (!klassPath.isEmpty()) { + klassPath.removeLast(); } } } diff --git a/leia-schema-validator/src/main/java/com/grookage/leia/validator/StaticSchemaValidator.java b/leia-schema-validator/src/main/java/com/grookage/leia/validator/StaticSchemaValidator.java index c8fe952..92bdd16 100644 --- a/leia-schema-validator/src/main/java/com/grookage/leia/validator/StaticSchemaValidator.java +++ b/leia-schema-validator/src/main/java/com/grookage/leia/validator/StaticSchemaValidator.java @@ -52,7 +52,7 @@ public StaticSchemaValidator(Supplier> supplier, } @SneakyThrows - private Set validate(final SchemaKey schemaKey, Class klass) { + private List validate(final SchemaKey schemaKey, Class klass) { final var details = supplier.get().stream() .filter(each -> each.match(schemaKey)).findFirst().orElse(null); if (null == details) { @@ -64,7 +64,7 @@ private Set validate(final SchemaKey schemaKey, Class kl @Override public void start() { log.info("Starting the schema validator"); - Map> violations = new HashMap<>(); + Map> violations = new HashMap<>(); packageRoots.forEach(handlerPackage -> { final var reflections = new Reflections(handlerPackage); final var annotatedClasses = reflections.getTypesAnnotatedWith(SchemaDefinition.class);