c1eaf60ad0df326f390fe077d154cb428db34be1
[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                                                           final String capabilityName) {
95         try {
96             if (NodeFilterConstraintAction.ADD == action || NodeFilterConstraintAction.UPDATE == action) {
97                 for (final String uiConstraint : uiConstraints) {
98                     final UIConstraint constraint = new ConstraintConvertor().convert(uiConstraint);
99                     if (ConstraintConvertor.PROPERTY_CONSTRAINT.equals(constraint.getSourceType())) {
100                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither = validatePropertyConstraint(parentComponent,
101                             componentInstanceId, constraint, capabilityName);
102                         if (booleanResponseFormatEither.isRight()) {
103                             return booleanResponseFormatEither;
104                         }
105                     }
106                     else if (ConstraintConvertor.SERVICE_INPUT_CONSTRAINT.equals(constraint.getSourceType())) {
107                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither = validateInputConstraint(parentComponent,
108                             componentInstanceId, constraint);
109                         if (booleanResponseFormatEither.isRight()) {
110                             return booleanResponseFormatEither;
111                         }
112                     }
113                     else if (ConstraintConvertor.STATIC_CONSTRAINT.equals(constraint.getSourceType())) {
114                         Either<Boolean, ResponseFormat> booleanResponseFormatEither;
115                         if (NodeFilterConstraintType.PROPERTIES.equals(nodeFilterConstraintType)) {
116                             booleanResponseFormatEither = isComponentPropertyFilterValid(parentComponent, componentInstanceId, constraint);
117                         } else {
118                             booleanResponseFormatEither = isComponentCapabilityPropertyFilterValid(parentComponent, componentInstanceId, constraint);
119                         }
120                         if (booleanResponseFormatEither.isRight()) {
121                             return booleanResponseFormatEither;
122                         }
123                     }
124                 }
125             }
126         } catch (final Exception e) {
127             LOGGER.debug("Provided constraint" + uiConstraints, e);
128             return Either.right(componentsUtils.getResponseFormat(ActionStatus.CONSTRAINT_FORMAT_INCORRECT));
129         }
130         return Either.left(true);
131     }
132
133     private Either<Boolean, ResponseFormat> isComponentCapabilityPropertyFilterValid(final Component parentComponent,
134                                                                                      final String componentInstanceId,
135                                                                                      final UIConstraint uiConstraint) {
136         return validateStaticValueAndOperatorOfCapabilityProperties(parentComponent, componentInstanceId, uiConstraint);
137     }
138
139     private Either<Boolean, ResponseFormat> isComponentPropertyFilterValid(Component parentComponent, String componentInstanceId,
140                                                                            UIConstraint constraint) {
141         return validateStaticValueAndOperator(parentComponent, componentInstanceId, constraint);
142     }
143
144     private Either<Boolean, ResponseFormat> validatePropertyConstraint(final Component parentComponent, final String componentInstanceId,
145                                                                        final UIConstraint uiConstraint, final String capabilityName) {
146         String source = SOURCE;
147         final Optional<ComponentInstance> optionalComponentInstance;
148         final List<PropertyDefinition> propertyDefinitions = parentComponent.getProperties();
149         final var SELF = "SELF";
150         List<? extends PropertyDefinition> sourcePropertyDefinition =
151             SELF.equalsIgnoreCase(uiConstraint.getSourceName()) && propertyDefinitions != null ? propertyDefinitions
152                 : Collections.emptyList();
153         if (sourcePropertyDefinition.isEmpty() && !SELF.equalsIgnoreCase(uiConstraint.getSourceName())) {
154             optionalComponentInstance = parentComponent.getComponentInstances().stream()
155                 .filter(componentInstance -> uiConstraint.getSourceName().equals(componentInstance.getName())).findFirst();
156             if (optionalComponentInstance.isPresent()) {
157                 final List<ComponentInstanceProperty> componentInstanceProperties = parentComponent.getComponentInstancesProperties()
158                     .get(optionalComponentInstance.get().getUniqueId());
159                 sourcePropertyDefinition = componentInstanceProperties == null ? new ArrayList<>() : componentInstanceProperties;
160             }
161         }
162         if (CollectionUtils.isNotEmpty(sourcePropertyDefinition)) {
163             final Optional<? extends PropertyDefinition> sourceSelectedProperty = sourcePropertyDefinition.stream()
164                 .filter(property -> uiConstraint.getValue().equals(property.getName())).findFirst();
165             Optional<? extends PropertyDefinition> targetComponentInstanceProperty = getProperty(parentComponent, componentInstanceId, capabilityName, uiConstraint.getServicePropertyName());
166            
167             source = !targetComponentInstanceProperty.isPresent() ? "Target" : SOURCE;
168             if (sourceSelectedProperty.isPresent() && targetComponentInstanceProperty.isPresent()) {
169                 return validatePropertyData(uiConstraint, sourceSelectedProperty, targetComponentInstanceProperty);
170             }
171         }
172         final String missingProperty = source.equals(SOURCE) ? uiConstraint.getValue().toString() : uiConstraint.getServicePropertyName();
173         return Either.right(componentsUtils.getResponseFormat(ActionStatus.MAPPED_PROPERTY_NOT_FOUND, source, missingProperty));
174     }
175     
176     private Optional<ComponentInstanceProperty> getProperty(final Component parentComponent, final String componentInstanceId,
177             final String capabilityName, final String propertyName) {
178
179         if (StringUtils.isEmpty(capabilityName)) {
180             return parentComponent.getComponentInstancesProperties().get(componentInstanceId).stream()
181                     .filter(property -> propertyName.equals(property.getName())).findFirst();
182         } else {
183             final Optional<ComponentInstance> componentInstanceOptional = parentComponent.getComponentInstances().stream()
184                     .filter(componentInstance -> componentInstance.getUniqueId().equals(componentInstanceId)).findAny();
185             if (componentInstanceOptional.isPresent()) {
186                 for (final List<CapabilityDefinition> listOfCaps : componentInstanceOptional.get().getCapabilities().values()) {
187                     final Optional<CapabilityDefinition> capDef = listOfCaps.stream().filter(cap -> cap.getName().equals(capabilityName)).findAny();
188                     if (capDef.isPresent()) {
189                         return capDef.get().getProperties().stream().filter(property -> propertyName.equals(property.getName())).findFirst();
190                     }
191                 }
192             }
193         }
194         return Optional.empty();
195     }
196
197     private Either<Boolean, ResponseFormat> validateInputConstraint(final Component parentComponent, final String componentInstanceId,
198                                                                     final UIConstraint uiConstraint) {
199         final List<InputDefinition> sourceInputDefinition = parentComponent.getInputs();
200         if (CollectionUtils.isNotEmpty(sourceInputDefinition)) {
201             final Optional<? extends InputDefinition> sourceSelectedProperty = sourceInputDefinition.stream()
202                 .filter(input -> uiConstraint.getValue().equals(input.getName())).findFirst();
203             final Optional<? extends PropertyDefinition> targetComponentInstanceProperty = parentComponent.getComponentInstancesProperties()
204                 .get(componentInstanceId).stream().filter(property -> uiConstraint.getServicePropertyName().equals(property.getName())).findFirst();
205             if (sourceSelectedProperty.isPresent() && targetComponentInstanceProperty.isPresent()) {
206                 return validatePropertyData(uiConstraint, sourceSelectedProperty, targetComponentInstanceProperty);
207             }
208         }
209         LOGGER.debug("Parent component does not have inputs", parentComponent);
210         return Either.right(componentsUtils.getResponseFormat(ActionStatus.INPUTS_NOT_FOUND));
211     }
212
213     private Either<Boolean, ResponseFormat> validatePropertyData(UIConstraint uiConstraint,
214                                                                  Optional<? extends PropertyDefinition> sourceSelectedProperty,
215                                                                  Optional<? extends PropertyDefinition> targetComponentInstanceProperty) {
216         if (sourceSelectedProperty.isPresent() && targetComponentInstanceProperty.isPresent()) {
217             final PropertyDefinition sourcePropDefinition = sourceSelectedProperty.get();
218             final String sourceType = sourcePropDefinition.getType();
219             final PropertyDefinition targetPropDefinition = targetComponentInstanceProperty.get();
220             final String targetType = targetPropDefinition.getType();
221             if (sourceType.equals(targetType)) {
222                 if (schemableTypes.contains(sourceType)) {
223                     final SchemaDefinition sourceSchemaDefinition = sourcePropDefinition.getSchema();
224                     final SchemaDefinition targetSchemaDefinition = targetPropDefinition.getSchema();
225                     if (!sourceSchemaDefinition.equals(targetSchemaDefinition)) {
226                         return Either.right(componentsUtils
227                             .getResponseFormat(ActionStatus.SOURCE_TARGET_SCHEMA_MISMATCH, uiConstraint.getServicePropertyName(),
228                                 uiConstraint.getValue().toString()));
229                     }
230                 }
231                 return Either.left(Boolean.TRUE);
232             } else {
233                 return Either.right(componentsUtils
234                     .getResponseFormat(ActionStatus.SOURCE_TARGET_PROPERTY_TYPE_MISMATCH, uiConstraint.getServicePropertyName(),
235                         uiConstraint.getValue().toString()));
236             }
237         } else {
238             LOGGER.debug("Null value passed to `validatePropertyData` - sourceSelectedProperty: '{}' - targetComponentInstanceProperty: '{}'",
239                 sourceSelectedProperty, targetComponentInstanceProperty);
240             return Either.right(componentsUtils
241                 .getResponseFormat(ActionStatus.GENERAL_ERROR, uiConstraint.getServicePropertyName(), uiConstraint.getValue().toString()));
242         }
243     }
244
245     private Either<Boolean, ResponseFormat> validateStaticValueAndOperator(final Component parentComponent, final String componentInstanceId,
246                                                                            final UIConstraint uiConstraint) {
247         if (!(Objects.nonNull(uiConstraint) && uiConstraint.getValue() instanceof String)) {
248             return Either.left(false);
249         }
250         //TODO: get capabilities properties when constraint type is capabilities
251         final Optional<ComponentInstanceProperty> componentInstanceProperty = parentComponent.getComponentInstancesProperties()
252             .get(componentInstanceId).stream().filter(property -> uiConstraint.getServicePropertyName().equals(property.getName())).findFirst();
253         if (!componentInstanceProperty.isPresent()) {
254             return Either.right(componentsUtils.getResponseFormat(ActionStatus.SELECTED_PROPERTY_NOT_PRESENT, uiConstraint.getServicePropertyName()));
255         }
256         if (comparableConstraintsOperators.contains(uiConstraint.getConstraintOperator()) && !comparableTypes
257             .contains(componentInstanceProperty.get().getType())) {
258             return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_OPERATOR_PROVIDED, uiConstraint.getServicePropertyName(),
259                 uiConstraint.getConstraintOperator()));
260         }
261         return isValidValueCheck(componentInstanceProperty.get().getType(), String.valueOf(uiConstraint.getValue()),
262             uiConstraint.getServicePropertyName());
263     }
264
265     private Either<Boolean, ResponseFormat> validateStaticValueAndOperatorOfCapabilityProperties(final Component parentComponent,
266                                                                                                  final String componentInstanceId,
267                                                                                                  final UIConstraint uiConstraint) {
268         if (!(Objects.nonNull(uiConstraint) && uiConstraint.getValue() instanceof String)) {
269             return Either.left(false);
270         }
271         Optional<ComponentInstanceProperty> optionalComponentInstanceProperty = Optional.empty();
272         final Optional<ComponentInstance> optionalComponentInstances = parentComponent.getComponentInstances().stream()
273             .filter(componentInstance -> componentInstanceId.equalsIgnoreCase(componentInstance.getUniqueId())).findFirst();
274         if (optionalComponentInstances.isPresent()) {
275             final Optional<List<CapabilityDefinition>> optionalCapabilityDefinitionList = optionalComponentInstances.get().getCapabilities().values()
276                 .stream().filter(capabilityDefinitions -> capabilityDefinitions.stream()
277                     .allMatch(capabilityDefinition -> capabilityDefinition.getProperties() != null)).collect(Collectors.toList()).stream().filter(
278                     capabilityDefinitions -> capabilityDefinitions.stream().allMatch(
279                         capabilityDefinition -> capabilityDefinition.getProperties().stream().anyMatch(
280                             componentInstanceProperty -> uiConstraint.getServicePropertyName()
281                                 .equalsIgnoreCase(componentInstanceProperty.getName())))).findFirst();
282             if (optionalCapabilityDefinitionList.isPresent() && !optionalCapabilityDefinitionList.get().isEmpty()) {
283                 optionalComponentInstanceProperty = getComponentInstanceProperty(optionalCapabilityDefinitionList.get().get(0), uiConstraint.getServicePropertyName());
284             }
285         }
286
287         if (optionalComponentInstanceProperty.isEmpty()) {
288             return Either.right(componentsUtils.getResponseFormat(ActionStatus.SELECTED_PROPERTY_NOT_PRESENT, uiConstraint.getServicePropertyName()));
289         }
290         if (comparableConstraintsOperators.contains(uiConstraint.getConstraintOperator()) && !comparableTypes
291             .contains(optionalComponentInstanceProperty.get().getType())) {
292             return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_OPERATOR_PROVIDED, uiConstraint.getServicePropertyName(),
293                 uiConstraint.getConstraintOperator()));
294         }
295         return isValidValueCheck(optionalComponentInstanceProperty.get().getType(), String.valueOf(uiConstraint.getValue()),
296             uiConstraint.getServicePropertyName());
297     }
298     
299     private Optional<ComponentInstanceProperty> getComponentInstanceProperty(CapabilityDefinition capabilityDefinition, final String propertyName){
300         return capabilityDefinition.getProperties().stream().filter(property -> property.getName().equals(propertyName)).findAny();
301     }
302
303     private Either<Boolean, ResponseFormat> isValidValueCheck(String type, String value, String propertyName) {
304         ToscaPropertyType toscaPropertyType = ToscaPropertyType.isValidType(type);
305         if (Objects.isNull(toscaPropertyType)) {
306             return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_PROPERTY_TYPE, type, propertyName));
307         }
308         if (toscaPropertyType.getValidator().isValid(value, null)) {
309             return Either.left(Boolean.TRUE);
310         }
311         return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_VALUE_PROVIDED, type, propertyName, value));
312     }
313
314     public Either<Boolean, ResponseFormat> validateComponentFilter(final Component component, final List<String> uiConstraints,
315                                                                    final NodeFilterConstraintAction action) {
316         try {
317             if (NodeFilterConstraintAction.ADD == action || NodeFilterConstraintAction.UPDATE == action) {
318                 for (final String uiConstraint : uiConstraints) {
319                     final UIConstraint constraint = new ConstraintConvertor().convert(uiConstraint);
320                     if (ConstraintConvertor.PROPERTY_CONSTRAINT.equals(constraint.getSourceType())) {
321                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither = validateComponentPropertyConstraint(component,
322                             constraint);
323                         if (booleanResponseFormatEither.isRight()) {
324                             return booleanResponseFormatEither;
325                         }
326                     } else if (ConstraintConvertor.STATIC_CONSTRAINT.equals(constraint.getSourceType())) {
327                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither = validateComponentStaticValueAndOperator(component,
328                             constraint);
329                         if (booleanResponseFormatEither.isRight()) {
330                             return booleanResponseFormatEither;
331                         }
332                     }
333                 }
334             }
335         } catch (final Exception e) {
336             LOGGER.debug("Provided constraint" + uiConstraints, e);
337             return Either.right(componentsUtils.getResponseFormat(ActionStatus.CONSTRAINT_FORMAT_INCORRECT));
338         }
339         return Either.left(true);
340     }
341
342     private Either<Boolean, ResponseFormat> validateComponentPropertyConstraint(final Component component, final UIConstraint uiConstraint) {
343         String source = SOURCE;
344         final List<PropertyDefinition> propertyDefinitions = component.getProperties();
345         List<? extends PropertyDefinition> sourcePropertyDefinition =
346             component.getName().equals(uiConstraint.getSourceName()) && propertyDefinitions != null ? propertyDefinitions : Collections.emptyList();
347         if (CollectionUtils.isNotEmpty(sourcePropertyDefinition)) {
348             final Optional<? extends PropertyDefinition> sourceSelectedProperty = sourcePropertyDefinition.stream()
349                 .filter(property -> uiConstraint.getValue().equals(property.getName())).findFirst();
350             final Optional<? extends PropertyDefinition> targetComponentProperty = component.getProperties().stream()
351                 .filter(property -> uiConstraint.getServicePropertyName().equals(property.getName())).findFirst();
352             source = !targetComponentProperty.isPresent() ? "Target" : SOURCE;
353             if (sourceSelectedProperty.isPresent() && targetComponentProperty.isPresent()) {
354                 return validatePropertyData(uiConstraint, sourceSelectedProperty, targetComponentProperty);
355             }
356         }
357         final String missingProperty = source.equals(SOURCE) ? uiConstraint.getValue().toString() : uiConstraint.getServicePropertyName();
358         return Either.right(componentsUtils.getResponseFormat(ActionStatus.MAPPED_PROPERTY_NOT_FOUND, source, missingProperty));
359     }
360
361     private Either<Boolean, ResponseFormat> validateComponentStaticValueAndOperator(final Component component, final UIConstraint uiConstraint) {
362         if (!(Objects.nonNull(uiConstraint) && uiConstraint.getValue() instanceof String)) {
363             return Either.left(false);
364         }
365         final Optional<PropertyDefinition> componentProperty = component.getProperties().stream()
366             .filter(property -> uiConstraint.getServicePropertyName().equals(property.getName())).findFirst();
367         if (componentProperty.isEmpty()) {
368             return Either.right(componentsUtils.getResponseFormat(ActionStatus.SELECTED_PROPERTY_NOT_PRESENT, uiConstraint.getServicePropertyName()));
369         }
370         if (comparableConstraintsOperators.contains(uiConstraint.getConstraintOperator()) && !comparableTypes
371             .contains(componentProperty.get().getType())) {
372             return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_OPERATOR_PROVIDED, uiConstraint.getServicePropertyName(),
373                 uiConstraint.getConstraintOperator()));
374         }
375         return isValidValueCheck(componentProperty.get().getType(), String.valueOf(uiConstraint.getValue()), uiConstraint.getServicePropertyName());
376     }
377 }