diff options
Diffstat (limited to 'src/main/java/org/onap/aai/validation/modeldriven/validator/ModelDrivenValidator.java')
-rw-r--r-- | src/main/java/org/onap/aai/validation/modeldriven/validator/ModelDrivenValidator.java | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/src/main/java/org/onap/aai/validation/modeldriven/validator/ModelDrivenValidator.java b/src/main/java/org/onap/aai/validation/modeldriven/validator/ModelDrivenValidator.java new file mode 100644 index 0000000..1b8ab00 --- /dev/null +++ b/src/main/java/org/onap/aai/validation/modeldriven/validator/ModelDrivenValidator.java @@ -0,0 +1,306 @@ +/* + * ============LICENSE_START=================================================== + * Copyright (c) 2018 Amdocs + * ============================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END===================================================== + */ +package org.onap.aai.validation.modeldriven.validator; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.gson.JsonObject; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import javax.inject.Inject; +import org.apache.commons.collections.CollectionUtils; +import org.dom4j.Node; +import org.onap.aai.validation.Validator; +import org.onap.aai.validation.exception.ValidationServiceException; +import org.onap.aai.validation.modeldriven.ModelCacheManager; +import org.onap.aai.validation.modeldriven.ModelId; +import org.onap.aai.validation.modeldriven.configuration.mapping.ModelInstanceMapper; +import org.onap.aai.validation.modeldriven.configuration.mapping.ModelInstanceMappingReader; +import org.onap.aai.validation.modeldriven.configuration.mapping.ModelInstanceMapper.MappingType; +import org.onap.aai.validation.reader.EntityReader; +import org.onap.aai.validation.reader.EventReader; +import org.onap.aai.validation.reader.InstanceEntityReader; +import org.onap.aai.validation.reader.data.Entity; +import org.onap.aai.validation.reader.data.EntityId; +import org.onap.aai.validation.result.ValidationResult; +import org.onap.aai.validation.result.Violation; +import org.onap.aai.validation.result.Violation.Builder; +import org.onap.aai.validation.result.Violation.ViolationType; + +/** + * Validates object instances against the ONAP model. + * + */ +public class ModelDrivenValidator implements Validator { + + /** + * Types of validation rules. + */ + public enum RuleType { + REL, ATTR + } + + /** + * Types of validation outcomes. + */ + public enum ValidationOutcomeType { + NO_MODEL, MISSING, UNEXPECTED; + } + + private ModelCacheManager modelCacheManager; + private List<ModelInstanceMapper> mappings; + private InstanceReader instanceReader; + private EventReader eventReader; + + /** + * Constructor defining injected dependencies. + * + * @param modelCacheManager + * a cache manager for the models + * @param modelInstanceMappingReader + * a configuration reader to provide model and instance mapping + * @param instanceReader + * a reader of A&AI instances + * @param eventReader + * @throws ValidationServiceException + */ + @Inject + public ModelDrivenValidator(ModelCacheManager modelCacheManager, ModelInstanceMappingReader modelInstanceMappingReader, InstanceReader instanceReader, + EventReader eventReader) throws ValidationServiceException { + this.modelCacheManager = modelCacheManager; + this.mappings = modelInstanceMappingReader.getMappings(); + this.instanceReader = instanceReader; + this.eventReader = eventReader; + } + + @Override + public void initialise() throws ValidationServiceException { + // Deliberately empty + } + + /** + * Validates the given event instance against the ECOMP model. + * + * @param eventInstance + * the event instance to be validated + * @return {@link Violation} with the results of the object validation + * @throws ValidationServiceException + */ + @Override + public List<ValidationResult> validate(String eventInstance) throws ValidationServiceException { + // Read event json into Entity bean + Entity eventEntity = eventReader.getEntity(eventInstance); + + EntityReader reader = new InstanceEntityReader(instanceReader); + String entityJson = instanceReader.getInstance(eventEntity.getJson(), mappings); + Entity instanceEntity = new Entity(entityJson, instanceReader.getInstanceType(entityJson), eventEntity.getEntityLink(), reader); + + // Get model ID from object instance and retrieve corresponding model. + ModelId modelId = new ModelId(ModelId.ATTR_MODEL_ID, instanceReader.getModelId(instanceEntity.getJson())); + Node modelElement = modelCacheManager.get(modelId); + + List<Violation> violations = new ArrayList<>(); + + if (modelElement == null) { + ViolationInfo info = ViolationInfo.valueOf(ModelDrivenValidator.ValidationOutcomeType.NO_MODEL.toString()); + Map<String, Object> details = new HashMap<>(); + details.put("No model ID", modelId.getModelId()); + Violation.Builder builder = new Violation.Builder(instanceEntity).violationType(ViolationType.MODEL); + builder.category(info.getCategory()).severity(info.getSeverity()).violationDetails(details) + .errorMessage(info.getErrorMessage(modelId.getModelId())); + violations.add(builder.build()); + } else { + // Validate model with instance according to mappings. + for (ModelInstanceMapper mapping : mappings) { + List<Violation> currentViolations = new ArrayList<>(); + + // If we are validating related objects, find the first valid child object to begin validating from. + Node validModelElement = modelElement; + if (MappingType.RELATIONSHIP.equals(mapping.getMappingType()) && !ModelReader.isValidModelType(modelElement, mapping)) { + Multimap<String, Node> models = HashMultimap.create(); + ModelReader.getValuesAndModels(modelElement, mapping, modelCacheManager, models); + validModelElement = models.isEmpty() ? modelElement : models.values().iterator().next(); + } + + validateAllRecursive(validModelElement, instanceEntity, mapping, currentViolations, reader); + violations.addAll(currentViolations); + } + } + + ValidationResult validationResult = new ValidationResult(instanceEntity); + + // This is a shortcut to passing the parent model name all the way down. + populateViolationModelNames(violations, instanceEntity); + + validationResult.addViolations(violations); + + return Arrays.asList(validationResult); + } + + /* + * Recursively validates all child entities starting with the crown widget. + */ + private void validateAllRecursive(Node currentModelNode, Entity entity, ModelInstanceMapper mapping, List<Violation> validations, EntityReader reader) + throws ValidationServiceException { + String entityLink = null; + Multimap<String, Node> modelMap = ModelReader.getValues(currentModelNode, mapping, modelCacheManager); + Multimap<String, String> instanceMap = instanceReader.getValues(entity.getJson(), mapping); + + // Validate model with instance according to mappings. + // Note: Currently the cardinality of instances are not validated. + List<Violation> currentViolations = validateModelInstanceValues(modelMap.keySet(), instanceMap.keySet(), entity, mapping); + validations.addAll(currentViolations); + + // Remove erroring objects from the maps so that we don't validate their children. + for (Violation currentViolation : currentViolations) { + String entityType = (String) currentViolation.getViolationDetails().get(Violation.ENTITY_TYPE_PROPERTY); + if (entityType != null) { + modelMap.removeAll(entityType); + instanceMap.removeAll(entityType); + } + } + + // Continue down the model hierarchy for objects that did not error in the current layer. + for (Entry<String, Node> modelEntry : modelMap.entries()) { + // Get the child model. + Node childModelNode = modelEntry.getValue(); + if (childModelNode != null) { + // Validate all child instance objects with current child model. + Collection<String> childInstanceObjects = instanceMap.get(modelEntry.getKey()); + for (String childInstanceObject : childInstanceObjects) { + Entity childEntity = new Entity(childInstanceObject, instanceReader.getInstanceType(childInstanceObject), entityLink, reader); + validateAllRecursive(childModelNode, childEntity, mapping, validations, reader); + } + } + } + } + + /** + * Compares the List of values found in the model with the values found in the Json and generates validation errors + * based on the differences. + * + * @param modelValues + * the values found in the model + * @param instanceValues + * the values found in the Json. + * @param entity + * @param modelInstanceMapper + * the mappings used to to find the model and instance values + * @return List of Validation objects representing the errors found during validation. + * @throws ValidationServiceException + */ + private List<Violation> validateModelInstanceValues(Set<String> modelValues, Set<String> instanceValues, Entity entity, + ModelInstanceMapper modelInstanceMapper) throws ValidationServiceException { + List<Violation> violations = new ArrayList<>(); + + Collection<?> missingValues = CollectionUtils.subtract(modelValues, instanceValues); + violations.addAll(setViolationDetails(ValidationOutcomeType.MISSING, missingValues, entity, modelInstanceMapper)); + + Collection<?> unexpectedValues = CollectionUtils.subtract(instanceValues, modelValues); + violations.addAll(setViolationDetails(ValidationOutcomeType.UNEXPECTED, unexpectedValues, entity, modelInstanceMapper)); + + return violations; + } + + /* + * Sets the list of {@link Violation} objects with all the violation details. + * + * @param outcome + * + * @param values the values that are causing the violation + * + * @param entity the entity being validated + * + * @param modelInstanceMapper the mappings used to to find the model and instance values + * + * @return list of {@link Violation} objects set by this method + * + * @throws ValidationServiceException + */ + private List<Violation> setViolationDetails(ValidationOutcomeType outcome, Collection<?> values, Entity entity, ModelInstanceMapper modelInstanceMapper) + throws ValidationServiceException { + + List<Violation> violations = new ArrayList<>(); + Builder builder = new Builder(entity).violationType("Model"); + + for (Object value : values) { + RuleType ruleType = modelInstanceMapper.getMappingType().equals(MappingType.RELATIONSHIP) ? RuleType.REL : RuleType.ATTR; + String category = outcome.toString() + "_" + ruleType; + + ViolationInfo info = ViolationInfo.valueOf(category); + builder.category(info.getCategory()); + builder.severity(info.getSeverity()); + + Map<String, Object> details = new HashMap<>(); + details.put(outcome.toString() + " " + ruleType.toString(), value); + + switch (ruleType) { + case ATTR: + builder.errorMessage(info.getErrorMessage(value)); + break; + case REL: + buildEntityIdDetails(details, entity); + builder.errorMessage(info.getErrorMessage(entity.getIds().toString(), entity.getType(), value)); + break; + default: + // Do nothing + break; + } + builder.violationDetails(details); + violations.add(builder.build()); + } + + return violations; + } + + /* + * Add entity IDs to the violation details + * + * @param details the violation details to populate + * + * @param entity + * + * @throws ValidationServiceException + */ + private void buildEntityIdDetails(Map<String, Object> details, Entity entity) throws ValidationServiceException { + JsonObject entityIdsObject = new JsonObject(); + for (EntityId entityId : entity.getIds()) { + entityIdsObject.addProperty(entityId.getPrimaryKey(), entityId.getValue()); + } + details.put(Violation.ENTITY_ID_PROPERTY, entityIdsObject); + details.put(Violation.ENTITY_TYPE_PROPERTY, entity.getType()); + details.put(Violation.ENTITY_MODELNAME_PROPERTY, instanceReader.getModelName(entity.getJson())); + } + + /* + * Sets the model name attribute on all violations in the list. The model name is retrieved from the entity + * provided. + */ + private void populateViolationModelNames(List<Violation> violations, Entity entity) { + String modelName = instanceReader.getModelName(entity.getJson()); + for (Violation violation : violations) { + violation.setModelName(modelName); + } + } +} |