db5de581290fee6d5ee98767547a4cc01455e89e
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / validation / NodeFilterValidator.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 package org.openecomp.sdc.be.components.validation;
21
22 import com.google.common.collect.ImmutableSet;
23 import fj.data.Either;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Objects;
28 import java.util.Optional;
29 import java.util.Set;
30 import java.util.stream.Collectors;
31 import org.apache.commons.collections4.CollectionUtils;
32 import org.apache.commons.lang3.StringUtils;
33 import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
34 import org.openecomp.sdc.be.components.impl.utils.NodeFilterConstraintAction;
35 import org.openecomp.sdc.be.dao.api.ActionStatus;
36 import org.openecomp.sdc.be.datamodel.utils.ConstraintConvertor;
37 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
38 import org.openecomp.sdc.be.datatypes.enums.NodeFilterConstraintType;
39 import org.openecomp.sdc.be.impl.ComponentsUtils;
40 import org.openecomp.sdc.be.model.CapabilityDefinition;
41 import org.openecomp.sdc.be.model.Component;
42 import org.openecomp.sdc.be.model.ComponentInstance;
43 import org.openecomp.sdc.be.model.ComponentInstanceProperty;
44 import org.openecomp.sdc.be.model.InputDefinition;
45 import org.openecomp.sdc.be.model.PropertyDefinition;
46 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade;
47 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
48 import org.openecomp.sdc.be.ui.model.UIConstraint;
49 import org.openecomp.sdc.exception.ResponseFormat;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52 import org.springframework.beans.factory.annotation.Autowired;
53
54 @org.springframework.stereotype.Component("NodeFilterValidator")
55 public class NodeFilterValidator {
56
57     private static final String SOURCE = "Source";
58     public static final Set<String> comparableTypes = ImmutableSet
59         .of(ToscaPropertyType.STRING.getType(), ToscaPropertyType.INTEGER.getType(), ToscaPropertyType.FLOAT.getType());
60     public static final Set<String> schemableTypes = ImmutableSet.of(ToscaPropertyType.MAP.getType(), ToscaPropertyType.LIST.getType());
61     public static final Set<String> comparableConstraintsOperators = ImmutableSet
62         .of(ConstraintConvertor.GREATER_THAN_OPERATOR, ConstraintConvertor.LESS_THAN_OPERATOR);
63     protected final ToscaOperationFacade toscaOperationFacade;
64     protected final ComponentsUtils componentsUtils;
65     private static final Logger LOGGER = LoggerFactory.getLogger(NodeFilterValidator.class);
66
67     @Autowired
68     public NodeFilterValidator(final ToscaOperationFacade toscaOperationFacade, final ComponentsUtils componentsUtils) {
69         this.toscaOperationFacade = toscaOperationFacade;
70         this.componentsUtils = componentsUtils;
71     }
72
73     public Either<Boolean, ResponseFormat> validateComponentInstanceExist(final Component component, final String componentInstanceId) {
74         if (component == null || StringUtils.isEmpty(componentInstanceId)) {
75             LOGGER.error("Input data cannot be empty");
76             return getErrorResponse(ActionStatus.FILTER_NOT_FOUND);
77         }
78         if (CollectionUtils.isEmpty(component.getComponentInstances()) || component.getComponentInstances().stream()
79             .noneMatch(ci -> ci.getUniqueId().equals(componentInstanceId))) {
80             LOGGER.error("Component Instance list is empty");
81             return getErrorResponse(ActionStatus.FILTER_NOT_FOUND);
82         }
83         return Either.left(Boolean.TRUE);
84     }
85
86     private Either<Boolean, ResponseFormat> getErrorResponse(ActionStatus actionStatus, String... variables) {
87         ResponseFormat errorResponse = ResponseFormatManager.getInstance().getResponseFormat(actionStatus, variables);
88         return Either.right(errorResponse);
89     }
90
91     public Either<Boolean, ResponseFormat> validateFilter(final Component parentComponent, final String componentInstanceId,
92                                                           final List<String> uiConstraints, final NodeFilterConstraintAction action,
93                                                           final NodeFilterConstraintType nodeFilterConstraintType) {
94         try {
95             if (NodeFilterConstraintAction.ADD == action || NodeFilterConstraintAction.UPDATE == action) {
96                 for (final String uiConstraint : uiConstraints) {
97                     final UIConstraint constraint = new ConstraintConvertor().convert(uiConstraint);
98                     if (ConstraintConvertor.PROPERTY_CONSTRAINT.equals(constraint.getSourceType())) {
99                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither = validatePropertyConstraint(parentComponent,
100                             componentInstanceId, constraint);
101                         if (booleanResponseFormatEither.isRight()) {
102                             return booleanResponseFormatEither;
103                         }
104                     }
105                     else if (ConstraintConvertor.SERVICE_INPUT_CONSTRAINT.equals(constraint.getSourceType())) {
106                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither = validateInputConstraint(parentComponent,
107                             componentInstanceId, constraint);
108                         if (booleanResponseFormatEither.isRight()) {
109                             return booleanResponseFormatEither;
110                         }
111                     }
112                     else if (ConstraintConvertor.STATIC_CONSTRAINT.equals(constraint.getSourceType())) {
113                         Either<Boolean, ResponseFormat> booleanResponseFormatEither;
114                         if (NodeFilterConstraintType.PROPERTIES.equals(nodeFilterConstraintType)) {
115                             booleanResponseFormatEither = isComponentPropertyFilterValid(parentComponent, componentInstanceId, constraint);
116                         } else {
117                             booleanResponseFormatEither = isComponentCapabilityPropertyFilterValid(parentComponent, componentInstanceId, constraint);
118                         }
119                         if (booleanResponseFormatEither.isRight()) {
120                             return booleanResponseFormatEither;
121                         }
122                     }
123                 }
124             }
125         } catch (final Exception e) {
126             LOGGER.debug("Provided constraint" + uiConstraints, e);
127             return Either.right(componentsUtils.getResponseFormat(ActionStatus.CONSTRAINT_FORMAT_INCORRECT));
128         }
129         return Either.left(true);
130     }
131
132     private Either<Boolean, ResponseFormat> isComponentCapabilityPropertyFilterValid(final Component parentComponent,
133                                                                                      final String componentInstanceId,
134                                                                                      final UIConstraint uiConstraint) {
135         return validateStaticValueAndOperatorOfCapabilityProperties(parentComponent, componentInstanceId, uiConstraint);
136     }
137
138     private Either<Boolean, ResponseFormat> isComponentPropertyFilterValid(Component parentComponent, String componentInstanceId,
139                                                                            UIConstraint constraint) {
140         return validateStaticValueAndOperator(parentComponent, componentInstanceId, constraint);
141     }
142
143     private Either<Boolean, ResponseFormat> validatePropertyConstraint(final Component parentComponent, final String componentInstanceId,
144                                                                        final UIConstraint uiConstraint) {
145         String source = SOURCE;
146         final Optional<ComponentInstance> optionalComponentInstance;
147         final List<PropertyDefinition> propertyDefinitions = parentComponent.getProperties();
148         final var SELF = "SELF";
149         List<? extends PropertyDefinition> sourcePropertyDefinition =
150             SELF.equalsIgnoreCase(uiConstraint.getSourceName()) && propertyDefinitions != null ? propertyDefinitions
151                 : Collections.emptyList();
152         if (sourcePropertyDefinition.isEmpty() && !SELF.equalsIgnoreCase(uiConstraint.getSourceName())) {
153             optionalComponentInstance = parentComponent.getComponentInstances().stream()
154                 .filter(componentInstance -> uiConstraint.getSourceName().equals(componentInstance.getName())).findFirst();
155             if (optionalComponentInstance.isPresent()) {
156                 final List<ComponentInstanceProperty> componentInstanceProperties = parentComponent.getComponentInstancesProperties()
157                     .get(optionalComponentInstance.get().getUniqueId());
158                 sourcePropertyDefinition = componentInstanceProperties == null ? new ArrayList<>() : componentInstanceProperties;
159             }
160         }
161         if (CollectionUtils.isNotEmpty(sourcePropertyDefinition)) {
162             final Optional<? extends PropertyDefinition> sourceSelectedProperty = sourcePropertyDefinition.stream()
163                 .filter(property -> uiConstraint.getValue().equals(property.getName())).findFirst();
164             final Optional<? extends PropertyDefinition> targetComponentInstanceProperty = parentComponent.getComponentInstancesProperties()
165                 .get(componentInstanceId).stream().filter(property -> uiConstraint.getServicePropertyName().equals(property.getName())).findFirst();
166             source = !targetComponentInstanceProperty.isPresent() ? "Target" : SOURCE;
167             if (sourceSelectedProperty.isPresent() && targetComponentInstanceProperty.isPresent()) {
168                 return validatePropertyData(uiConstraint, sourceSelectedProperty, targetComponentInstanceProperty);
169             }
170         }
171         final String missingProperty = source.equals(SOURCE) ? uiConstraint.getValue().toString() : uiConstraint.getServicePropertyName();
172         return Either.right(componentsUtils.getResponseFormat(ActionStatus.MAPPED_PROPERTY_NOT_FOUND, source, missingProperty));
173     }
174
175     private Either<Boolean, ResponseFormat> validateInputConstraint(final Component parentComponent, final String componentInstanceId,
176                                                                     final UIConstraint uiConstraint) {
177         final List<InputDefinition> sourceInputDefinition = parentComponent.getInputs();
178         if (CollectionUtils.isNotEmpty(sourceInputDefinition)) {
179             final Optional<? extends InputDefinition> sourceSelectedProperty = sourceInputDefinition.stream()
180                 .filter(input -> uiConstraint.getValue().equals(input.getName())).findFirst();
181             final Optional<? extends PropertyDefinition> targetComponentInstanceProperty = parentComponent.getComponentInstancesProperties()
182                 .get(componentInstanceId).stream().filter(property -> uiConstraint.getServicePropertyName().equals(property.getName())).findFirst();
183             if (sourceSelectedProperty.isPresent() && targetComponentInstanceProperty.isPresent()) {
184                 return validatePropertyData(uiConstraint, sourceSelectedProperty, targetComponentInstanceProperty);
185             }
186         }
187         LOGGER.debug("Parent component does not have inputs", parentComponent);
188         return Either.right(componentsUtils.getResponseFormat(ActionStatus.INPUTS_NOT_FOUND));
189     }
190
191     private Either<Boolean, ResponseFormat> validatePropertyData(UIConstraint uiConstraint,
192                                                                  Optional<? extends PropertyDefinition> sourceSelectedProperty,
193                                                                  Optional<? extends PropertyDefinition> targetComponentInstanceProperty) {
194         if (sourceSelectedProperty.isPresent() && targetComponentInstanceProperty.isPresent()) {
195             final PropertyDefinition sourcePropDefinition = sourceSelectedProperty.get();
196             final String sourceType = sourcePropDefinition.getType();
197             final PropertyDefinition targetPropDefinition = targetComponentInstanceProperty.get();
198             final String targetType = targetPropDefinition.getType();
199             if (sourceType.equals(targetType)) {
200                 if (schemableTypes.contains(sourceType)) {
201                     final SchemaDefinition sourceSchemaDefinition = sourcePropDefinition.getSchema();
202                     final SchemaDefinition targetSchemaDefinition = targetPropDefinition.getSchema();
203                     if (!sourceSchemaDefinition.equals(targetSchemaDefinition)) {
204                         return Either.right(componentsUtils
205                             .getResponseFormat(ActionStatus.SOURCE_TARGET_SCHEMA_MISMATCH, uiConstraint.getServicePropertyName(),
206                                 uiConstraint.getValue().toString()));
207                     }
208                 }
209                 return Either.left(Boolean.TRUE);
210             } else {
211                 return Either.right(componentsUtils
212                     .getResponseFormat(ActionStatus.SOURCE_TARGET_PROPERTY_TYPE_MISMATCH, uiConstraint.getServicePropertyName(),
213                         uiConstraint.getValue().toString()));
214             }
215         } else {
216             LOGGER.debug("Null value passed to `validatePropertyData` - sourceSelectedProperty: '{}' - targetComponentInstanceProperty: '{}'",
217                 sourceSelectedProperty, targetComponentInstanceProperty);
218             return Either.right(componentsUtils
219                 .getResponseFormat(ActionStatus.GENERAL_ERROR, uiConstraint.getServicePropertyName(), uiConstraint.getValue().toString()));
220         }
221     }
222
223     private Either<Boolean, ResponseFormat> validateStaticValueAndOperator(final Component parentComponent, final String componentInstanceId,
224                                                                            final UIConstraint uiConstraint) {
225         if (!(Objects.nonNull(uiConstraint) && uiConstraint.getValue() instanceof String)) {
226             return Either.left(false);
227         }
228         //TODO: get capabilities properties when constraint type is capabilities
229         final Optional<ComponentInstanceProperty> componentInstanceProperty = parentComponent.getComponentInstancesProperties()
230             .get(componentInstanceId).stream().filter(property -> uiConstraint.getServicePropertyName().equals(property.getName())).findFirst();
231         if (!componentInstanceProperty.isPresent()) {
232             return Either.right(componentsUtils.getResponseFormat(ActionStatus.SELECTED_PROPERTY_NOT_PRESENT, uiConstraint.getServicePropertyName()));
233         }
234         if (comparableConstraintsOperators.contains(uiConstraint.getConstraintOperator()) && !comparableTypes
235             .contains(componentInstanceProperty.get().getType())) {
236             return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_OPERATOR_PROVIDED, uiConstraint.getServicePropertyName(),
237                 uiConstraint.getConstraintOperator()));
238         }
239         return isValidValueCheck(componentInstanceProperty.get().getType(), String.valueOf(uiConstraint.getValue()),
240             uiConstraint.getServicePropertyName());
241     }
242
243     private Either<Boolean, ResponseFormat> validateStaticValueAndOperatorOfCapabilityProperties(final Component parentComponent,
244                                                                                                  final String componentInstanceId,
245                                                                                                  final UIConstraint uiConstraint) {
246         if (!(Objects.nonNull(uiConstraint) && uiConstraint.getValue() instanceof String)) {
247             return Either.left(false);
248         }
249         Optional<ComponentInstanceProperty> optionalComponentInstanceProperty = Optional.empty();
250         final Optional<ComponentInstance> optionalComponentInstances = parentComponent.getComponentInstances().stream()
251             .filter(componentInstance -> componentInstanceId.equalsIgnoreCase(componentInstance.getUniqueId())).findFirst();
252         if (optionalComponentInstances.isPresent()) {
253             final Optional<List<CapabilityDefinition>> optionalCapabilityDefinitionList = optionalComponentInstances.get().getCapabilities().values()
254                 .stream().filter(capabilityDefinitions -> capabilityDefinitions.stream()
255                     .allMatch(capabilityDefinition -> capabilityDefinition.getProperties() != null)).collect(Collectors.toList()).stream().filter(
256                     capabilityDefinitions -> capabilityDefinitions.stream().allMatch(
257                         capabilityDefinition -> capabilityDefinition.getProperties().stream().anyMatch(
258                             componentInstanceProperty -> uiConstraint.getServicePropertyName()
259                                 .equalsIgnoreCase(componentInstanceProperty.getName())))).findFirst();
260             if (optionalCapabilityDefinitionList.isPresent() && !optionalCapabilityDefinitionList.get().isEmpty()) {
261                 optionalComponentInstanceProperty = getComponentInstanceProperty(optionalCapabilityDefinitionList.get().get(0), uiConstraint.getServicePropertyName());
262             }
263         }
264
265         if (optionalComponentInstanceProperty.isEmpty()) {
266             return Either.right(componentsUtils.getResponseFormat(ActionStatus.SELECTED_PROPERTY_NOT_PRESENT, uiConstraint.getServicePropertyName()));
267         }
268         if (comparableConstraintsOperators.contains(uiConstraint.getConstraintOperator()) && !comparableTypes
269             .contains(optionalComponentInstanceProperty.get().getType())) {
270             return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_OPERATOR_PROVIDED, uiConstraint.getServicePropertyName(),
271                 uiConstraint.getConstraintOperator()));
272         }
273         return isValidValueCheck(optionalComponentInstanceProperty.get().getType(), String.valueOf(uiConstraint.getValue()),
274             uiConstraint.getServicePropertyName());
275     }
276     
277     private Optional<ComponentInstanceProperty> getComponentInstanceProperty(CapabilityDefinition capabilityDefinition, final String propertyName){
278         return capabilityDefinition.getProperties().stream().filter(property -> property.getName().equals(propertyName)).findAny();
279     }
280
281     private Either<Boolean, ResponseFormat> isValidValueCheck(String type, String value, String propertyName) {
282         ToscaPropertyType toscaPropertyType = ToscaPropertyType.isValidType(type);
283         if (Objects.isNull(toscaPropertyType)) {
284             return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_PROPERTY_TYPE, type, propertyName));
285         }
286         if (toscaPropertyType.getValidator().isValid(value, null)) {
287             return Either.left(Boolean.TRUE);
288         }
289         return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_VALUE_PROVIDED, type, propertyName, value));
290     }
291
292     public Either<Boolean, ResponseFormat> validateComponentFilter(final Component component, final List<String> uiConstraints,
293                                                                    final NodeFilterConstraintAction action) {
294         try {
295             if (NodeFilterConstraintAction.ADD == action || NodeFilterConstraintAction.UPDATE == action) {
296                 for (final String uiConstraint : uiConstraints) {
297                     final UIConstraint constraint = new ConstraintConvertor().convert(uiConstraint);
298                     if (ConstraintConvertor.PROPERTY_CONSTRAINT.equals(constraint.getSourceType())) {
299                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither = validateComponentPropertyConstraint(component,
300                             constraint);
301                         if (booleanResponseFormatEither.isRight()) {
302                             return booleanResponseFormatEither;
303                         }
304                     } else if (ConstraintConvertor.STATIC_CONSTRAINT.equals(constraint.getSourceType())) {
305                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither = validateComponentStaticValueAndOperator(component,
306                             constraint);
307                         if (booleanResponseFormatEither.isRight()) {
308                             return booleanResponseFormatEither;
309                         }
310                     }
311                 }
312             }
313         } catch (final Exception e) {
314             LOGGER.debug("Provided constraint" + uiConstraints, e);
315             return Either.right(componentsUtils.getResponseFormat(ActionStatus.CONSTRAINT_FORMAT_INCORRECT));
316         }
317         return Either.left(true);
318     }
319
320     private Either<Boolean, ResponseFormat> validateComponentPropertyConstraint(final Component component, final UIConstraint uiConstraint) {
321         String source = SOURCE;
322         final List<PropertyDefinition> propertyDefinitions = component.getProperties();
323         List<? extends PropertyDefinition> sourcePropertyDefinition =
324             component.getName().equals(uiConstraint.getSourceName()) && propertyDefinitions != null ? propertyDefinitions : Collections.emptyList();
325         if (CollectionUtils.isNotEmpty(sourcePropertyDefinition)) {
326             final Optional<? extends PropertyDefinition> sourceSelectedProperty = sourcePropertyDefinition.stream()
327                 .filter(property -> uiConstraint.getValue().equals(property.getName())).findFirst();
328             final Optional<? extends PropertyDefinition> targetComponentProperty = component.getProperties().stream()
329                 .filter(property -> uiConstraint.getServicePropertyName().equals(property.getName())).findFirst();
330             source = !targetComponentProperty.isPresent() ? "Target" : SOURCE;
331             if (sourceSelectedProperty.isPresent() && targetComponentProperty.isPresent()) {
332                 return validatePropertyData(uiConstraint, sourceSelectedProperty, targetComponentProperty);
333             }
334         }
335         final String missingProperty = source.equals(SOURCE) ? uiConstraint.getValue().toString() : uiConstraint.getServicePropertyName();
336         return Either.right(componentsUtils.getResponseFormat(ActionStatus.MAPPED_PROPERTY_NOT_FOUND, source, missingProperty));
337     }
338
339     private Either<Boolean, ResponseFormat> validateComponentStaticValueAndOperator(final Component component, final UIConstraint uiConstraint) {
340         if (!(Objects.nonNull(uiConstraint) && uiConstraint.getValue() instanceof String)) {
341             return Either.left(false);
342         }
343         final Optional<PropertyDefinition> componentProperty = component.getProperties().stream()
344             .filter(property -> uiConstraint.getServicePropertyName().equals(property.getName())).findFirst();
345         if (componentProperty.isEmpty()) {
346             return Either.right(componentsUtils.getResponseFormat(ActionStatus.SELECTED_PROPERTY_NOT_PRESENT, uiConstraint.getServicePropertyName()));
347         }
348         if (comparableConstraintsOperators.contains(uiConstraint.getConstraintOperator()) && !comparableTypes
349             .contains(componentProperty.get().getType())) {
350             return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_OPERATOR_PROVIDED, uiConstraint.getServicePropertyName(),
351                 uiConstraint.getConstraintOperator()));
352         }
353         return isValidValueCheck(componentProperty.get().getType(), String.valueOf(uiConstraint.getValue()), uiConstraint.getServicePropertyName());
354     }
355 }