Fix for substitution filter properties
[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
21 package org.openecomp.sdc.be.components.validation;
22
23 import com.google.common.collect.ImmutableSet;
24 import fj.data.Either;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Objects;
29 import java.util.Optional;
30 import java.util.Set;
31 import org.apache.commons.lang3.StringUtils;
32 import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
33 import org.openecomp.sdc.be.components.impl.utils.NodeFilterConstraintAction;
34 import org.openecomp.sdc.be.dao.api.ActionStatus;
35 import org.openecomp.sdc.be.datamodel.utils.ConstraintConvertor;
36 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
37 import org.openecomp.sdc.be.impl.ComponentsUtils;
38 import org.openecomp.sdc.be.model.Component;
39 import org.openecomp.sdc.be.model.ComponentInstance;
40 import org.openecomp.sdc.be.model.ComponentInstanceProperty;
41 import org.openecomp.sdc.be.model.PropertyDefinition;
42 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade;
43 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
44 import org.openecomp.sdc.be.ui.model.UIConstraint;
45 import org.openecomp.sdc.exception.ResponseFormat;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.springframework.beans.factory.annotation.Autowired;
49 import org.springframework.util.CollectionUtils;
50
51 @org.springframework.stereotype.Component("NodeFilterValidator")
52 public class NodeFilterValidator {
53
54     private static final String SOURCE = "Source";
55     public static final Set<String> comparableTypes = ImmutableSet.of(ToscaPropertyType.STRING.getType(),
56             ToscaPropertyType.INTEGER.getType(), ToscaPropertyType.FLOAT.getType());
57     public static final Set<String> schemableTypes =
58             ImmutableSet.of(ToscaPropertyType.MAP.getType(), ToscaPropertyType.LIST.getType());
59     public static final Set<String> comparableConstraintsOperators =
60             ImmutableSet.of(ConstraintConvertor.GREATER_THAN_OPERATOR, ConstraintConvertor.LESS_THAN_OPERATOR);
61
62     protected final ToscaOperationFacade toscaOperationFacade;
63     protected final ComponentsUtils componentsUtils;
64
65     private static final Logger LOGGER = LoggerFactory.getLogger(NodeFilterValidator.class);
66
67     @Autowired
68     public NodeFilterValidator(final ToscaOperationFacade toscaOperationFacade,
69                                final ComponentsUtils componentsUtils) {
70         this.toscaOperationFacade = toscaOperationFacade;
71         this.componentsUtils = componentsUtils;
72     }
73
74     public Either<Boolean, ResponseFormat> validateComponentInstanceExist(final Component component,
75                                                                           final String componentInstanceId) {
76         if (component == null || StringUtils.isEmpty(componentInstanceId)) {
77             LOGGER.error("Input data cannot be empty");
78             return getErrorResponse(ActionStatus.FILTER_NOT_FOUND);
79         }
80         if (CollectionUtils.isEmpty(component.getComponentInstances()) ||
81                 component.getComponentInstances().stream()
82                         .noneMatch(ci -> ci.getUniqueId().equals(componentInstanceId))) {
83             LOGGER.error("Component Instance list is empty");
84             return getErrorResponse(ActionStatus.FILTER_NOT_FOUND);
85         }
86         return Either.left(Boolean.TRUE);
87     }
88
89     private Either<Boolean, ResponseFormat> getErrorResponse(ActionStatus actionStatus, String... variables) {
90         ResponseFormat errorResponse = ResponseFormatManager.getInstance().getResponseFormat(actionStatus, variables);
91         return Either.right(errorResponse);
92     }
93
94     public Either<Boolean, ResponseFormat> validateFilter(final Component parentComponent,
95                                                           final String componentInstanceId,
96                                                           final List<String> uiConstraints,
97                                                           final NodeFilterConstraintAction action) {
98         try {
99             if (NodeFilterConstraintAction.ADD == action || NodeFilterConstraintAction.UPDATE == action) {
100                 for (final String uiConstraint : uiConstraints) {
101                     final UIConstraint constraint = new ConstraintConvertor().convert(uiConstraint);
102                     if (ConstraintConvertor.PROPERTY_CONSTRAINT.equals(constraint.getSourceType())) {
103                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither =
104                                 validatePropertyConstraint(parentComponent, componentInstanceId, constraint);
105                         if (booleanResponseFormatEither.isRight()) {
106                             return booleanResponseFormatEither;
107                         }
108                     } else if (ConstraintConvertor.STATIC_CONSTRAINT.equals(constraint.getSourceType())) {
109                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither =
110                                 validateStaticValueAndOperator(parentComponent, componentInstanceId, constraint);
111                         if (booleanResponseFormatEither.isRight()) {
112                             return booleanResponseFormatEither;
113                         }
114                     }
115                 }
116             }
117         } catch (final Exception e) {
118             LOGGER.debug("Provided constraint" + uiConstraints, e);
119             return Either.right(componentsUtils.getResponseFormat(ActionStatus.CONSTRAINT_FORMAT_INCORRECT));
120         }
121
122         return Either.left(true);
123     }
124
125     private Either<Boolean, ResponseFormat> validatePropertyConstraint(final Component parentComponent,
126                                                                        final String componentInstanceId,
127                                                                        final UIConstraint uiConstraint) {
128         String source = SOURCE;
129         final Optional<ComponentInstance> optionalComponentInstance;
130
131         final List<PropertyDefinition> propertyDefinitions = parentComponent.getProperties();
132         List<? extends PropertyDefinition> sourcePropertyDefinition =
133                 parentComponent.getName().equals(uiConstraint.getSourceName()) &&
134                         propertyDefinitions != null ? propertyDefinitions : Collections.emptyList();
135
136         if (sourcePropertyDefinition.isEmpty() && !parentComponent.getName().equals(uiConstraint.getSourceName())) {
137             optionalComponentInstance = parentComponent.getComponentInstances().stream()
138                     .filter(componentInstance -> uiConstraint.getSourceName()
139                             .equals(componentInstance
140                                     .getName()))
141                     .findFirst();
142
143             if (optionalComponentInstance.isPresent()) {
144                 final List<ComponentInstanceProperty> componentInstanceProperties =
145                         parentComponent.getComponentInstancesProperties()
146                                 .get(optionalComponentInstance.get().getUniqueId());
147                 sourcePropertyDefinition =
148                         componentInstanceProperties == null ? new ArrayList<>() : componentInstanceProperties;
149             }
150         }
151
152         if (!CollectionUtils.isEmpty(sourcePropertyDefinition)) {
153             final Optional<? extends PropertyDefinition> sourceSelectedProperty = sourcePropertyDefinition.stream()
154                     .filter(property -> uiConstraint
155                             .getValue()
156                             .equals(property.getName()))
157                     .findFirst();
158
159             final Optional<? extends PropertyDefinition> targetComponentInstanceProperty =
160                     parentComponent.getComponentInstancesProperties().get(componentInstanceId).stream()
161                             .filter(property -> uiConstraint.getServicePropertyName().equals(property.getName()))
162                             .findFirst();
163
164             source = !targetComponentInstanceProperty.isPresent() ? "Target" : SOURCE;
165             if (sourceSelectedProperty.isPresent() && targetComponentInstanceProperty.isPresent()) {
166                 return validatePropertyData(uiConstraint, sourceSelectedProperty, targetComponentInstanceProperty);
167             }
168         }
169
170         final String missingProperty =
171                 source.equals(SOURCE) ? uiConstraint.getValue().toString() : uiConstraint.getServicePropertyName();
172
173         return Either.right(
174                 componentsUtils.getResponseFormat(ActionStatus.MAPPED_PROPERTY_NOT_FOUND, source, missingProperty));
175     }
176
177     private Either<Boolean, ResponseFormat> validatePropertyData(UIConstraint uiConstraint,
178                                                                  Optional<? extends PropertyDefinition> sourceSelectedProperty,
179                                                                  Optional<? extends PropertyDefinition> targetComponentInstanceProperty) {
180         if (sourceSelectedProperty.isPresent() && targetComponentInstanceProperty.isPresent()) {
181             final PropertyDefinition sourcePropDefinition = sourceSelectedProperty.get();
182             final String sourceType = sourcePropDefinition.getType();
183             final PropertyDefinition targetPropDefinition = targetComponentInstanceProperty.get();
184             final String targetType = targetPropDefinition.getType();
185             if (sourceType.equals(targetType)) {
186                 if (schemableTypes.contains(sourceType)) {
187                     final SchemaDefinition sourceSchemaDefinition = sourcePropDefinition.getSchema();
188                     final SchemaDefinition targetSchemaDefinition = targetPropDefinition.getSchema();
189                     if (!sourceSchemaDefinition.equals(targetSchemaDefinition)) {
190                         return Either
191                                 .right(componentsUtils.getResponseFormat(ActionStatus.SOURCE_TARGET_SCHEMA_MISMATCH,
192                                         uiConstraint.getServicePropertyName(), uiConstraint.getValue().toString()));
193                     }
194                 }
195                 return Either.left(Boolean.TRUE);
196             } else {
197                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.SOURCE_TARGET_PROPERTY_TYPE_MISMATCH,
198                         uiConstraint.getServicePropertyName(), uiConstraint.getValue().toString()));
199             }
200         } else {
201             LOGGER.debug(
202                     "Null value passed to `validatePropertyData` - sourceSelectedProperty: '{}' - targetComponentInstanceProperty: '{}'",
203                     sourceSelectedProperty, targetComponentInstanceProperty);
204             return Either.right(componentsUtils
205                     .getResponseFormat(ActionStatus.GENERAL_ERROR, uiConstraint.getServicePropertyName(),
206                             uiConstraint.getValue().toString()));
207         }
208     }
209
210     private Either<Boolean, ResponseFormat> validateStaticValueAndOperator(
211             final Component parentComponent,
212             final String componentInstanceId, final UIConstraint uiConstraint) {
213         if (!(Objects.nonNull(uiConstraint) && uiConstraint.getValue() instanceof String)) {
214             return Either.left(false);
215         }
216         final Optional<ComponentInstanceProperty> componentInstanceProperty =
217                 parentComponent.getComponentInstancesProperties().get(componentInstanceId).stream()
218                         .filter(property -> uiConstraint.getServicePropertyName().equals(property.getName()))
219                         .findFirst();
220
221         if (!componentInstanceProperty.isPresent()) {
222             return Either.right(componentsUtils.getResponseFormat(ActionStatus.SELECTED_PROPERTY_NOT_PRESENT,
223                     uiConstraint.getServicePropertyName()));
224         }
225         if (comparableConstraintsOperators.contains(uiConstraint.getConstraintOperator()) && !comparableTypes.contains(
226                 componentInstanceProperty.get().getType())) {
227             return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_OPERATOR_PROVIDED,
228                     uiConstraint.getServicePropertyName(), uiConstraint.getConstraintOperator()));
229         }
230
231         return isValidValueCheck(componentInstanceProperty.get().getType(), String.valueOf(uiConstraint.getValue()),
232                 uiConstraint.getServicePropertyName());
233     }
234
235     private Either<Boolean, ResponseFormat> isValidValueCheck(String type, String value, String propertyName) {
236
237         ToscaPropertyType toscaPropertyType = ToscaPropertyType.isValidType(type);
238         if (Objects.isNull(toscaPropertyType)) {
239             return Either.right(
240                     componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_PROPERTY_TYPE, type, propertyName));
241         }
242         if (toscaPropertyType.getValidator().isValid(value, null)) {
243             return Either.left(Boolean.TRUE);
244         }
245         return Either.right(
246                 componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_VALUE_PROVIDED, type, propertyName, value));
247     }
248
249     public Either<Boolean, ResponseFormat> validateComponentFilter(final Component component,
250                                                                    final List<String> uiConstraints,
251                                                                    final NodeFilterConstraintAction action) {
252         try {
253             if (NodeFilterConstraintAction.ADD == action || NodeFilterConstraintAction.UPDATE == action) {
254                 for (final String uiConstraint : uiConstraints) {
255                     final UIConstraint constraint = new ConstraintConvertor().convert(uiConstraint);
256                     if (ConstraintConvertor.PROPERTY_CONSTRAINT.equals(constraint.getSourceType())) {
257                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither =
258                                 validateComponentPropertyConstraint(component, constraint);
259                         if (booleanResponseFormatEither.isRight()) {
260                             return booleanResponseFormatEither;
261                         }
262                     } else if (ConstraintConvertor.STATIC_CONSTRAINT.equals(constraint.getSourceType())) {
263                         final Either<Boolean, ResponseFormat> booleanResponseFormatEither =
264                                 validateComponentStaticValueAndOperator(component, constraint);
265                         if (booleanResponseFormatEither.isRight()) {
266                             return booleanResponseFormatEither;
267                         }
268                     }
269                 }
270             }
271         } catch (final Exception e) {
272             LOGGER.debug("Provided constraint" + uiConstraints, e);
273             return Either.right(componentsUtils.getResponseFormat(ActionStatus.CONSTRAINT_FORMAT_INCORRECT));
274         }
275
276         return Either.left(true);
277     }
278
279     private Either<Boolean, ResponseFormat> validateComponentPropertyConstraint(final Component component,
280                                                                                 final UIConstraint uiConstraint) {
281         String source = SOURCE;
282
283         final List<PropertyDefinition> propertyDefinitions = component.getProperties();
284         List<? extends PropertyDefinition> sourcePropertyDefinition =
285                 component.getName().equals(uiConstraint.getSourceName()) &&
286                         propertyDefinitions != null ? propertyDefinitions : Collections.emptyList();
287
288         if (!CollectionUtils.isEmpty(sourcePropertyDefinition)) {
289             final Optional<? extends PropertyDefinition> sourceSelectedProperty = sourcePropertyDefinition.stream()
290                     .filter(property -> uiConstraint
291                             .getValue()
292                             .equals(property.getName()))
293                     .findFirst();
294
295             final Optional<? extends PropertyDefinition> targetComponentProperty =
296                     component.getProperties().stream()
297                             .filter(property -> uiConstraint.getServicePropertyName().equals(property.getName()))
298                             .findFirst();
299
300             source = !targetComponentProperty.isPresent() ? "Target" : SOURCE;
301             if (sourceSelectedProperty.isPresent() && targetComponentProperty.isPresent()) {
302                 return validatePropertyData(uiConstraint, sourceSelectedProperty, targetComponentProperty);
303             }
304         }
305
306         final String missingProperty =
307                 source.equals(SOURCE) ? uiConstraint.getValue().toString() : uiConstraint.getServicePropertyName();
308
309         return Either.right(
310                 componentsUtils.getResponseFormat(ActionStatus.MAPPED_PROPERTY_NOT_FOUND, source, missingProperty));
311     }
312
313     private Either<Boolean, ResponseFormat> validateComponentStaticValueAndOperator(
314             final Component component, final UIConstraint uiConstraint) {
315         if (!(Objects.nonNull(uiConstraint) && uiConstraint.getValue() instanceof String)) {
316             return Either.left(false);
317         }
318         final Optional<PropertyDefinition> componentProperty =
319                 component.getProperties().stream()
320                         .filter(property -> uiConstraint.getServicePropertyName().equals(property.getName()))
321                         .findFirst();
322
323         if (componentProperty.isEmpty()) {
324             return Either.right(componentsUtils.getResponseFormat(ActionStatus.SELECTED_PROPERTY_NOT_PRESENT,
325                     uiConstraint.getServicePropertyName()));
326         }
327         if (comparableConstraintsOperators.contains(uiConstraint.getConstraintOperator()) && !comparableTypes.contains(
328                 componentProperty.get().getType())) {
329             return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_OPERATOR_PROVIDED,
330                     uiConstraint.getServicePropertyName(), uiConstraint.getConstraintOperator()));
331         }
332
333         return isValidValueCheck(componentProperty.get().getType(), String.valueOf(uiConstraint.getValue()),
334                 uiConstraint.getServicePropertyName());
335     }
336
337 }