Interface operation feature enhancements
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / validation / InterfaceOperationValidation.java
1 /*
2  * Copyright © 2016-2018 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.openecomp.sdc.be.components.validation;
18
19 import fj.data.Either;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Objects;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.regex.Pattern;
29 import java.util.stream.Stream;
30 import org.apache.commons.collections.CollectionUtils;
31 import org.apache.commons.lang.StringUtils;
32 import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
33 import org.openecomp.sdc.be.dao.api.ActionStatus;
34 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
35 import org.openecomp.sdc.be.model.InputDefinition;
36 import org.openecomp.sdc.be.model.InterfaceDefinition;
37 import org.openecomp.sdc.be.model.Operation;
38 import org.openecomp.sdc.exception.ResponseFormat;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41 import org.springframework.stereotype.Component;
42
43 @Component("interfaceOperationValidation")
44 public class InterfaceOperationValidation {
45
46     private static final String TYPE_VALIDATION_REGEX = "^[a-zA-Z0-9_]{1,200}$";
47
48     private static final Logger LOGGER = LoggerFactory.getLogger(InterfaceOperationValidation.class);
49
50     public Either<Boolean, ResponseFormat> validateInterfaceOperations(InterfaceDefinition inputInterfaceDefinition,
51             org.openecomp.sdc.be.model.Component component, InterfaceDefinition storedInterfaceDefinition,
52             Map<String, InterfaceDefinition> globalInterfaceTypes, boolean isUpdate) {
53
54         Either<Boolean, ResponseFormat> validateAllowedOperationCountOnLocalInterfaceType =
55                 validateAllowedOperationCountOnLocalInterfaceType(inputInterfaceDefinition, storedInterfaceDefinition,
56                         globalInterfaceTypes, isUpdate);
57         if (validateAllowedOperationCountOnLocalInterfaceType.isRight()) {
58             return validateAllowedOperationCountOnLocalInterfaceType;
59         }
60
61         Either<Boolean, ResponseFormat> validateAllowedOperationsOnGlobalInterfaceType =
62                 validateAllowedOperationsOnGlobalInterfaceType(inputInterfaceDefinition, globalInterfaceTypes);
63         if (validateAllowedOperationsOnGlobalInterfaceType.isRight()) {
64             return validateAllowedOperationsOnGlobalInterfaceType;
65         }
66
67         Either<Boolean, ResponseFormat> validateOperationNameUniqueness =
68                 validateOperationNameUniquenessInCollection(inputInterfaceDefinition.getOperationsMap().values());
69         if (validateOperationNameUniqueness.isRight()) {
70             return validateOperationNameUniqueness;
71         }
72
73         for (Operation interfaceOperation : inputInterfaceDefinition.getOperationsMap().values()) {
74             Either<Boolean, ResponseFormat> interfaceOperationValidatorResponse =
75                     validateInterfaceOperation(interfaceOperation, storedInterfaceDefinition, component, isUpdate);
76             if (interfaceOperationValidatorResponse.isRight()) {
77                 return interfaceOperationValidatorResponse;
78             }
79         }
80
81         return Either.left(Boolean.TRUE);
82     }
83
84     private Either<Boolean, ResponseFormat> validateAllowedOperationCountOnLocalInterfaceType(
85             InterfaceDefinition inputInterfaceDefinition, InterfaceDefinition storedInterfaceDefinition,
86             Map<String, InterfaceDefinition> globalInterfaceTypes, boolean isUpdate) {
87
88         boolean isInterfaceTypeExistInGlobalType =
89                 globalInterfaceTypes.values().stream().map(InterfaceDefinition::getType)
90                         .anyMatch(type -> type.equalsIgnoreCase(inputInterfaceDefinition.getType()));
91         if (!isInterfaceTypeExistInGlobalType
92                     && (inputInterfaceDefinition.getOperations().size() > 1
93                                 || (!isUpdate && storedInterfaceDefinition != null
94                                       && storedInterfaceDefinition.getType()
95                                                  .equalsIgnoreCase(inputInterfaceDefinition.getType())))) {
96             return Either.right(getResponseFormatManager()
97                                         .getResponseFormat(ActionStatus.INTERFACE_OPERATION_INVALID_FOR_LOCAL_TYPE,
98                                                 inputInterfaceDefinition.getType()));
99         }
100
101         return Either.left(Boolean.TRUE);
102     }
103
104     private Either<Boolean, ResponseFormat> validateAllowedOperationsOnGlobalInterfaceType(
105             InterfaceDefinition interfaceDefinition, Map<String, InterfaceDefinition> globalInterfaceTypes) {
106
107         if (globalInterfaceTypes != null) {
108             boolean isOperationValidOnGlobalInterfaceType =
109                     Stream.of(interfaceDefinition)
110                             .filter(interfaceDef -> globalInterfaceTypes.values().stream().anyMatch(
111                                     interfaceDef1 -> interfaceDef1.getType().equalsIgnoreCase(interfaceDef.getType())))
112                             .flatMap(interfaceDef -> interfaceDef.getOperationsMap().values().stream()
113                                                              .map(Operation::getName))
114                             .allMatch(operationName -> globalInterfaceTypes.values().stream()
115                                                                .flatMap(interfaceDef -> interfaceDef.getOperationsMap()
116                                                                                                 .keySet().stream())
117                                                                .anyMatch(opName ->
118                                                                                  opName.equalsIgnoreCase(
119                                                                                          operationName)));
120             if (!isOperationValidOnGlobalInterfaceType) {
121                 return Either.right(getResponseFormatManager()
122                                             .getResponseFormat(ActionStatus.INTERFACE_OPERATION_INVALID_FOR_GLOBAL_TYPE,
123                                                     interfaceDefinition.getType()));
124             }
125         }
126         return Either.left(Boolean.TRUE);
127     }
128
129     private Either<Boolean, ResponseFormat> validateOperationNameUniquenessInCollection(
130             Collection<Operation> operationList) {
131         HashSet<String> operationNames = new HashSet<>();
132         for (Operation operation : operationList) {
133             if (!operationNames.add(operation.getName())) {
134                 return Either.right(getResponseFormatManager()
135                                             .getResponseFormat(ActionStatus.INTERFACE_OPERATION_NAME_ALREADY_IN_USE,
136                                                     operation.getName()));
137             }
138         }
139         return Either.left(Boolean.TRUE);
140     }
141
142     private Either<Boolean, ResponseFormat> validateInterfaceOperation(Operation interfaceOperation,
143             InterfaceDefinition interfaceDefinition, org.openecomp.sdc.be.model.Component component, boolean isUpdate) {
144
145         ResponseFormatManager responseFormatManager = getResponseFormatManager();
146         Either<Boolean, ResponseFormat> interfaceOperationTypeResponse =
147                 isInterfaceOperationTypeValid(interfaceOperation, responseFormatManager, interfaceDefinition, isUpdate);
148         if (interfaceOperationTypeResponse.isRight()) {
149             return Either.right(interfaceOperationTypeResponse.right().value());
150         }
151
152         if (null != interfaceOperation.getInputs()) {
153             Either<Boolean, ResponseFormat> inputParametersResponse =
154                     validateInputParameters(interfaceOperation, responseFormatManager);
155             if (inputParametersResponse.isRight()) {
156                 return Either.right(inputParametersResponse.right().value());
157             }
158
159             Either<Boolean, ResponseFormat> inputPropertyExistInComponent =
160                     validateInputPropertyExistInComponent(interfaceOperation, component, responseFormatManager);
161             if (inputPropertyExistInComponent.isRight()) {
162                 return Either.right(inputPropertyExistInComponent.right().value());
163
164             }
165         }
166
167         if (null != interfaceOperation.getOutputs()) {
168             Either<Boolean, ResponseFormat> outputParametersResponse =
169                     validateOutputParameters(interfaceOperation, responseFormatManager);
170             if (outputParametersResponse.isRight()) {
171                 return Either.right(outputParametersResponse.right().value());
172             }
173         }
174
175         return Either.left(Boolean.TRUE);
176     }
177
178     protected ResponseFormatManager getResponseFormatManager() {
179         return ResponseFormatManager.getInstance();
180     }
181
182     private Either<Boolean, ResponseFormat> isInterfaceOperationTypeValid(Operation interfaceOperation,
183             ResponseFormatManager responseFormatManager, InterfaceDefinition interfaceDefinition, boolean isUpdate) {
184
185         Either<Boolean, ResponseFormat> operationTypeEmptyEither =
186                 isOperationTypeEmpty(responseFormatManager, interfaceOperation.getName());
187         if (operationTypeEmptyEither.isRight()) {
188             return Either.right(operationTypeEmptyEither.right().value());
189         }
190
191         Either<Boolean, ResponseFormat> operationTypeRegexValidationResponse =
192                 isOperationTypeRegexValid(responseFormatManager, interfaceOperation.getName());
193         if (operationTypeRegexValidationResponse.isRight()) {
194             return Either.right(operationTypeRegexValidationResponse.right().value());
195         }
196
197         Either<Boolean, ResponseFormat> operationTypeUniqueResponse =
198                 validateOperationTypeUnique(interfaceOperation, interfaceDefinition, isUpdate);
199         if (operationTypeUniqueResponse.isRight()) {
200             return Either.right(operationTypeUniqueResponse.right().value());
201         }
202         if (!operationTypeUniqueResponse.left().value()) {
203             LOGGER.error("Interface Operation type  {} already in use ", interfaceOperation.getName());
204             ResponseFormat errorResponse = responseFormatManager.getResponseFormat(
205                     ActionStatus.INTERFACE_OPERATION_NAME_ALREADY_IN_USE, interfaceOperation.getName());
206             return Either.right(errorResponse);
207         }
208         return Either.left(Boolean.TRUE);
209     }
210
211     private Either<Boolean, ResponseFormat> validateInputParameters(Operation interfaceOperation,
212             ResponseFormatManager responseFormatManager) {
213         if (isInputParameterNameEmpty(interfaceOperation)) {
214             LOGGER.error("Interface operation input parameter name can't be empty");
215             ResponseFormat inputResponse =
216                     responseFormatManager.getResponseFormat(ActionStatus.INTERFACE_OPERATION_INPUT_NAME_MANDATORY);
217             return Either.right(inputResponse);
218         }
219
220         Either<Boolean, Set<String>> validateInputParametersUniqueResponse =
221                 isInputParametersUnique(interfaceOperation);
222         if (validateInputParametersUniqueResponse.isRight()) {
223             LOGGER.error("Interface operation input parameter names {} already in use",
224                     validateInputParametersUniqueResponse.right().value());
225             ResponseFormat inputResponse = responseFormatManager.getResponseFormat(
226                     ActionStatus.INTERFACE_OPERATION_INPUT_NAME_ALREADY_IN_USE,
227                     validateInputParametersUniqueResponse.right().value().toString());
228             return Either.right(inputResponse);
229         }
230         return Either.left(Boolean.TRUE);
231     }
232
233     private Either<Boolean, ResponseFormat> validateInputPropertyExistInComponent(Operation operation,
234             org.openecomp.sdc.be.model.Component component, ResponseFormatManager responseFormatManager) {
235
236         List<OperationInputDefinition> inputListToscaDataDefinition =
237                 operation.getInputs().getListToscaDataDefinition();
238         for (OperationInputDefinition inputDefinition : inputListToscaDataDefinition) {
239             if (!validateInputExistsInComponent(inputDefinition, component.getInputs())) {
240                 String missingPropertyName = inputDefinition.getInputId().contains(".")
241                                                      ? inputDefinition.getInputId().substring(
242                                                              inputDefinition.getInputId().indexOf('.') + 1)
243                                                      : inputDefinition.getInputId();
244                 LOGGER.error("Interface operation input property {} not found in component input properties",
245                         missingPropertyName);
246                 ResponseFormat inputResponse = responseFormatManager.getResponseFormat(
247                         ActionStatus.INTERFACE_OPERATION_INPUT_PROPERTY_NOT_FOUND_IN_COMPONENT, missingPropertyName,
248                         component.getComponentType().getValue());
249                 return Either.right(inputResponse);
250             }
251         }
252         return Either.left(Boolean.TRUE);
253     }
254
255     private Either<Boolean, ResponseFormat> validateOutputParameters(Operation interfaceOperation,
256             ResponseFormatManager responseFormatManager) {
257         if (isOutputParameterNameEmpty(interfaceOperation)) {
258             LOGGER.error("Interface operation output parameter name can't be empty");
259             ResponseFormat inputResponse =
260                     responseFormatManager.getResponseFormat(ActionStatus.INTERFACE_OPERATION_OUTPUT_NAME_MANDATORY);
261             return Either.right(inputResponse);
262         }
263
264         Either<Boolean, Set<String>> validateOutputParametersUniqueResponse =
265                 isOutputParametersUnique(interfaceOperation);
266         if (validateOutputParametersUniqueResponse.isRight()) {
267             LOGGER.error("Interface operation output parameter names {} already in use",
268                     validateOutputParametersUniqueResponse.right().value());
269             ResponseFormat inputResponse = responseFormatManager.getResponseFormat(
270                     ActionStatus.INTERFACE_OPERATION_OUTPUT_NAME_ALREADY_IN_USE,
271                     validateOutputParametersUniqueResponse.right().value().toString());
272             return Either.right(inputResponse);
273         }
274         return Either.left(Boolean.TRUE);
275     }
276
277     private Either<Boolean, ResponseFormat> isOperationTypeEmpty(ResponseFormatManager responseFormatManager,
278             String operationType) {
279         if (StringUtils.isEmpty(operationType)) {
280             LOGGER.error("Interface Operation type is mandatory");
281             ResponseFormat errorResponse =
282                     responseFormatManager.getResponseFormat(ActionStatus.INTERFACE_OPERATION_NAME_MANDATORY);
283             return Either.right(errorResponse);
284         }
285         return Either.left(Boolean.TRUE);
286     }
287
288     private Either<Boolean, ResponseFormat> isOperationTypeRegexValid(ResponseFormatManager responseFormatManager,
289             String operationType) {
290         if (!isValidOperationType(operationType)) {
291             LOGGER.error("Interface Operation type {} is invalid, Operation type should not contain"
292                                  + "Special character, space, numbers and  should not be greater than 200 characters",
293                     operationType);
294             ResponseFormat errorResponse = responseFormatManager
295                                                    .getResponseFormat(ActionStatus.INTERFACE_OPERATION_NAME_INVALID,
296                                                            operationType);
297             return Either.right(errorResponse);
298         }
299         return Either.left(Boolean.TRUE);
300     }
301
302     private Either<Boolean, ResponseFormat> validateOperationTypeUnique(Operation interfaceOperation,
303             InterfaceDefinition interfaceDefinition, boolean isUpdate) {
304         boolean isOperationTypeUnique = false;
305
306         if (interfaceDefinition == null || CollectionUtils.isEmpty(interfaceDefinition.getOperationsMap().values())) {
307             return Either.left(true);
308         }
309
310         Map<String, String> operationTypes = new HashMap<>();
311         interfaceDefinition.getOperationsMap().values()
312                 .forEach(operationType -> operationTypes.put(operationType.getUniqueId(), operationType.getName()));
313
314         if (!operationTypes.values().contains(interfaceOperation.getName())) {
315             isOperationTypeUnique = true;
316         }
317         if (!isOperationTypeUnique && isUpdate) {
318             Optional<String> id = operationTypes.entrySet().stream().filter(entry -> Objects.equals(entry.getValue(),
319                     interfaceOperation.getName())).map(Map.Entry::getKey).findAny();
320             if (id.isPresent() && id.get().equalsIgnoreCase(interfaceOperation.getUniqueId())) {
321                 isOperationTypeUnique = true;
322             }
323         }
324
325         return Either.left(isOperationTypeUnique);
326     }
327
328     private Boolean isInputParameterNameEmpty(Operation operationDataDefinition) {
329         return operationDataDefinition.getInputs().getListToscaDataDefinition().stream().anyMatch(
330                 inputParam -> inputParam.getName() == null || inputParam.getName().trim().equals(StringUtils.EMPTY));
331     }
332
333     private Either<Boolean, Set<String>> isInputParametersUnique(Operation operationDataDefinition) {
334         Set<String> inputParamNamesSet = new HashSet<>();
335         Set<String> duplicateParamNamesToReturn = new HashSet<>();
336         operationDataDefinition.getInputs().getListToscaDataDefinition().forEach(inputParam -> {
337             if (!inputParamNamesSet.add(inputParam.getName().trim())) {
338                 duplicateParamNamesToReturn.add(inputParam.getName().trim());
339             }
340         });
341         if (!duplicateParamNamesToReturn.isEmpty()) {
342             return Either.right(duplicateParamNamesToReturn);
343         }
344         return Either.left(Boolean.TRUE);
345     }
346
347     private boolean validateInputExistsInComponent(OperationInputDefinition input, List<InputDefinition> inputs) {
348         return inputs.stream().anyMatch(inp -> inp.getUniqueId().equals(input.getInputId()))
349                        || (input.getInputId().contains(".")
350                                    && inputs.stream()
351                                    .anyMatch(inp -> inp.getUniqueId()
352                                                             .equals(input.getInputId()
353                                                                             .substring(0,
354                                                                                     input.getInputId()
355                                                                                             .lastIndexOf('.')))));
356     }
357
358     private Boolean isOutputParameterNameEmpty(Operation operationDataDefinition) {
359         return operationDataDefinition.getOutputs().getListToscaDataDefinition().stream().anyMatch(
360                 outputParam -> outputParam.getName() == null || outputParam.getName().trim().equals(StringUtils.EMPTY));
361     }
362
363     private Either<Boolean, Set<String>> isOutputParametersUnique(Operation operationDataDefinition) {
364         Set<String> outputParamNamesSet = new HashSet<>();
365         Set<String> duplicateParamNamesToReturn = new HashSet<>();
366         operationDataDefinition.getOutputs().getListToscaDataDefinition().forEach(outputParam -> {
367             if (!outputParamNamesSet.add(outputParam.getName().trim())) {
368                 duplicateParamNamesToReturn.add(outputParam.getName().trim());
369             }
370         });
371         if (!duplicateParamNamesToReturn.isEmpty()) {
372             return Either.right(duplicateParamNamesToReturn);
373         }
374         return Either.left(Boolean.TRUE);
375     }
376
377     private boolean isValidOperationType(String operationType) {
378         return Pattern.matches(TYPE_VALIDATION_REGEX, operationType);
379     }
380
381 }