Interface operation support for service - BE
[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 org.apache.commons.collections.CollectionUtils;
21 import org.apache.commons.collections.MapUtils;
22 import org.apache.commons.lang.StringUtils;
23 import org.elasticsearch.common.Strings;
24 import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
25 import org.openecomp.sdc.be.dao.api.ActionStatus;
26 import org.openecomp.sdc.be.datatypes.elements.ListDataDefinition;
27 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
28 import org.openecomp.sdc.be.model.InputDefinition;
29 import org.openecomp.sdc.be.model.InterfaceDefinition;
30 import org.openecomp.sdc.be.model.Operation;
31 import org.openecomp.sdc.exception.ResponseFormat;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34 import org.springframework.stereotype.Component;
35
36 import java.util.Collection;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Objects;
42 import java.util.Optional;
43 import java.util.Set;
44 import java.util.regex.Pattern;
45 import java.util.stream.Collectors;
46
47 @Component("interfaceOperationValidation")
48 public class InterfaceOperationValidation {
49
50     private static final String TYPE_VALIDATION_REGEX = "^[a-zA-Z]{1,200}$";
51     private static final int DESCRIPTION_MAX_LENGTH = 200;
52
53     private static final Logger LOGGER = LoggerFactory.getLogger(InterfaceOperationValidation.class);
54
55     public Either<Boolean, ResponseFormat> validateInterfaceOperations(
56         Collection<Operation> interfaceOperations, org.openecomp.sdc.be.model.Component component, boolean isUpdate) {
57
58         for(Operation interfaceOperation : interfaceOperations) {
59             Either<Boolean, ResponseFormat> interfaceOperationValidatorResponse = validateInterfaceOperation(
60                 interfaceOperation, component, isUpdate);
61             if (interfaceOperationValidatorResponse.isRight()) {
62                 return interfaceOperationValidatorResponse;
63             }
64         }
65         return Either.left(Boolean.TRUE);
66     }
67
68     private Either<Boolean, ResponseFormat> validateInterfaceOperation(Operation interfaceOperation,
69         org.openecomp.sdc.be.model.Component component, boolean isUpdate) {
70         ResponseFormatManager responseFormatManager = getResponseFormatManager();
71
72         Either<Boolean, ResponseFormat> interfaceOperationTypeResponse = isInterfaceOperationTypeValid(interfaceOperation,
73             responseFormatManager, component, isUpdate);
74         if (interfaceOperationTypeResponse.isRight()) {
75             return Either.right(interfaceOperationTypeResponse.right().value());
76         }
77
78         Either<Boolean, ResponseFormat> descriptionResponseEither = isValidDescription(responseFormatManager,
79             interfaceOperation.getDescription());
80         if (descriptionResponseEither.isRight()) {
81             return Either.right(descriptionResponseEither.right().value());
82         }
83
84         Either<Boolean, ResponseFormat> inputPropertyExistInComponent = validateInputPropertyExistInComponent(interfaceOperation,
85                 component.getInputs(), responseFormatManager);
86         if(inputPropertyExistInComponent.isRight()) {
87             return Either.right(inputPropertyExistInComponent.right().value());
88
89         }
90
91         Either<Boolean, ResponseFormat> inputParametersResponse = validateInputParameters(interfaceOperation,
92             responseFormatManager);
93         if(inputParametersResponse.isRight()) {
94             return Either.right(inputParametersResponse.right().value());
95         }
96
97         Either<Boolean, ResponseFormat> outputParametersResponse = validateOutputParameters(interfaceOperation,
98             responseFormatManager);
99         if(outputParametersResponse.isRight()) {
100             return Either.right(outputParametersResponse.right().value());
101         }
102
103         return Either.left(Boolean.TRUE);
104     }
105
106     private Either<Boolean, ResponseFormat> isInterfaceOperationTypeValid(Operation interfaceOperation,
107         ResponseFormatManager responseFormatManager,
108         org.openecomp.sdc.be.model.Component component, boolean isUpdate) {
109
110         Either<Boolean, ResponseFormat> operationTypeEmptyEither =
111             isOperationTypeEmpty(responseFormatManager, interfaceOperation.getName());
112         if (operationTypeEmptyEither.isRight()) {
113             return Either.right(operationTypeEmptyEither.right().value());
114         }
115
116         Either<Boolean, ResponseFormat> operationTypeRegexValidationResponse =
117             isOperationTypeRegexValid(responseFormatManager, interfaceOperation.getName());
118         if (operationTypeRegexValidationResponse.isRight()) {
119             return Either.right(operationTypeRegexValidationResponse.right().value());
120         }
121
122         Either<Boolean, ResponseFormat> operationTypeUniqueResponse = validateOperationTypeUnique(interfaceOperation,
123             component, isUpdate );
124         if(operationTypeUniqueResponse.isRight()) {
125             return Either.right(operationTypeUniqueResponse.right().value());
126         }
127         if (!operationTypeUniqueResponse.left().value()) {
128             LOGGER.error("Interface Operation type  {} already in use ", interfaceOperation.getName());
129             ResponseFormat errorResponse = responseFormatManager.getResponseFormat(ActionStatus
130                 .INTERFACE_OPERATION_NAME_ALREADY_IN_USE, interfaceOperation.getName());
131             return Either.right(errorResponse);
132         }
133         return Either.left(Boolean.TRUE);
134     }
135
136     private Either<Boolean, ResponseFormat> isOperationTypeRegexValid(ResponseFormatManager responseFormatManager,
137         String operationType) {
138         if (!isValidOperationType(operationType)) {
139             LOGGER.error("Interface Operation type {} is invalid, Operation type should not contain" +
140                 "Special character, space, numbers and  should not be greater than 200 characters", operationType);
141             ResponseFormat errorResponse = responseFormatManager.getResponseFormat(ActionStatus
142                 .INTERFACE_OPERATION_NAME_INVALID, operationType);
143             return Either.right(errorResponse);
144         }
145         return Either.left(Boolean.TRUE);
146     }
147
148     private Either<Boolean, ResponseFormat> isOperationTypeEmpty(ResponseFormatManager responseFormatManager,
149         String operationType) {
150         if (StringUtils.isEmpty(operationType)) {
151             LOGGER.error("Interface Operation type is mandatory");
152             ResponseFormat errorResponse = responseFormatManager.getResponseFormat(ActionStatus
153                 .INTERFACE_OPERATION_NAME_MANDATORY);
154             return Either.right(errorResponse);
155         }
156         return Either.left(Boolean.TRUE);
157     }
158
159     private Either<Boolean, ResponseFormat> isValidDescription(ResponseFormatManager responseFormatManager,
160         String description) {
161         if (!Strings.isNullOrEmpty(description) && description.length() > DESCRIPTION_MAX_LENGTH) {
162             LOGGER.error("Interface Operation description {} is invalid, maximum 200 characters allowed", description);
163             ResponseFormat errorResponse = responseFormatManager.getResponseFormat(ActionStatus
164                 .INTERFACE_OPERATION_DESCRIPTION_MAX_LENGTH, description);
165             return Either.right(errorResponse);
166         }
167         return Either.left(Boolean.TRUE);
168     }
169
170     private boolean isValidOperationType(String operationType) {
171         return Pattern.matches(TYPE_VALIDATION_REGEX, operationType);
172     }
173
174     private Either<Boolean, ResponseFormat> validateOperationTypeUnique(
175         Operation interfaceOperation,
176         org.openecomp.sdc.be.model.Component component,
177         boolean isUpdate) {
178         boolean isOperationTypeUnique = false;
179
180         Map<String, InterfaceDefinition> interfaceDefinitionMap = component.getInterfaces();
181         if(interfaceDefinitionMap.isEmpty()){
182             return Either.left(true);
183         }
184
185         Collection<Operation> allOperations = interfaceDefinitionMap.values().stream()
186             .filter(a -> MapUtils.isNotEmpty(a.getOperationsMap()))
187             .map(a -> a.getOperationsMap().values()).flatMap(Collection::stream)
188             .collect(Collectors.toList());
189         if(CollectionUtils.isEmpty(allOperations)){
190             return Either.left(true);
191         }
192
193         Map<String, String> operationTypes = new HashMap<>();
194         allOperations.forEach(operationType -> operationTypes.put(operationType.getUniqueId(), operationType.getName()));
195
196         if (!operationTypes.values().contains(interfaceOperation.getName())){
197             isOperationTypeUnique = true;
198         }
199         if (!isOperationTypeUnique && isUpdate){
200             Optional<String> id = operationTypes.entrySet().stream().filter(entry -> Objects.equals(entry.getValue(), interfaceOperation.getName()))
201                 .map(Map.Entry::getKey).findAny();
202             if(id.isPresent() && id.get().equalsIgnoreCase(interfaceOperation.getUniqueId())){
203                 isOperationTypeUnique = true;
204             }
205         }
206
207         return Either.left(isOperationTypeUnique);
208     }
209     private Either<Boolean, ResponseFormat> validateInputParameters(Operation interfaceOperation,
210         ResponseFormatManager responseFormatManager) {
211         if (isInputParameterNameEmpty(interfaceOperation)) {
212             LOGGER.error("Interface operation input parameter name can't be empty");
213             ResponseFormat inputResponse = responseFormatManager.getResponseFormat(ActionStatus.INTERFACE_OPERATION_INPUT_NAME_MANDATORY);
214             return Either.right(inputResponse);
215         }
216
217         Either<Boolean, Set<String>> validateInputParametersUniqueResponse = isInputParametersUnique(interfaceOperation);
218         if(validateInputParametersUniqueResponse.isRight()) {
219             LOGGER.error("Interface operation input parameter names {} already in use",
220                 validateInputParametersUniqueResponse.right().value());
221             ResponseFormat inputResponse = responseFormatManager.getResponseFormat(ActionStatus
222                 .INTERFACE_OPERATION_INPUT_NAME_ALREADY_IN_USE, validateInputParametersUniqueResponse.right().value().toString());
223             return Either.right(inputResponse);
224         }
225         return Either.left(Boolean.TRUE);
226     }
227
228
229     private Either<Boolean, ResponseFormat> validateOutputParameters(Operation interfaceOperation,
230         ResponseFormatManager responseFormatManager) {
231         if (isOutputParameterNameEmpty(interfaceOperation)) {
232             LOGGER.error("Interface operation output parameter name can't be empty");
233             ResponseFormat inputResponse = responseFormatManager.getResponseFormat(ActionStatus.INTERFACE_OPERATION_OUTPUT_NAME_MANDATORY);
234             return Either.right(inputResponse);
235         }
236
237         Either<Boolean, Set<String>> validateOutputParametersUniqueResponse = isOutputParametersUnique(interfaceOperation);
238         if(validateOutputParametersUniqueResponse.isRight()) {
239             LOGGER.error("Interface operation output parameter names {} already in use",
240                 validateOutputParametersUniqueResponse.right().value());
241             ResponseFormat inputResponse = responseFormatManager.getResponseFormat(ActionStatus
242                 .INTERFACE_OPERATION_OUTPUT_NAME_ALREADY_IN_USE, validateOutputParametersUniqueResponse.right().value().toString());
243             return Either.right(inputResponse);
244         }
245         return Either.left(Boolean.TRUE);
246     }
247     private Either<Boolean, Set<String>> isInputParametersUnique(Operation operationDataDefinition) {
248         Set<String> inputParamNamesSet = new HashSet<>();
249         Set<String> duplicateParamNamesToReturn = new HashSet<>();
250         operationDataDefinition.getInputs().getListToscaDataDefinition()
251             .forEach(inputParam -> {
252                 if(!inputParamNamesSet.add(inputParam.getName().trim())) {
253                     duplicateParamNamesToReturn.add(inputParam.getName().trim());
254                 }
255             });
256         if(!duplicateParamNamesToReturn.isEmpty()) {
257             return Either.right(duplicateParamNamesToReturn);
258         }
259         return Either.left(Boolean.TRUE);
260     }
261
262     private Either<Boolean, Set<String>> isOutputParametersUnique(Operation operationDataDefinition) {
263         Set<String> outputParamNamesSet = new HashSet<>();
264         Set<String> duplicateParamNamesToReturn = new HashSet<>();
265         operationDataDefinition.getOutputs().getListToscaDataDefinition()
266             .forEach(outputParam -> {
267                 if(!outputParamNamesSet.add(outputParam.getName().trim())) {
268                     duplicateParamNamesToReturn.add(outputParam.getName().trim());
269                 }
270             });
271         if(!duplicateParamNamesToReturn.isEmpty()) {
272             return Either.right(duplicateParamNamesToReturn);
273         }
274         return Either.left(Boolean.TRUE);
275     }
276
277     private Boolean isInputParameterNameEmpty(Operation operationDataDefinition) {
278         return operationDataDefinition.getInputs().getListToscaDataDefinition().stream()
279             .anyMatch(inputParam -> inputParam.getName() == null || inputParam.getName().trim().equals(StringUtils.EMPTY));
280     }
281     private Boolean isOutputParameterNameEmpty(Operation operationDataDefinition) {
282         return operationDataDefinition.getInputs().getListToscaDataDefinition().stream()
283             .anyMatch(inputParam -> inputParam.getName() == null || inputParam.getName().trim().equals(StringUtils.EMPTY));
284     }
285
286     private  Either<Boolean, ResponseFormat> validateInputPropertyExistInComponent(Operation operation,
287                                                     List<InputDefinition> inputs,
288                                                     ResponseFormatManager responseFormatManager) {
289         ListDataDefinition<OperationInputDefinition> inputDefinitionListDataDefinition = operation.getInputs();
290         if (inputDefinitionListDataDefinition == null) {
291             return Either.left(Boolean.TRUE);
292         }
293         List<OperationInputDefinition> inputListToscaDataDefinition = inputDefinitionListDataDefinition.getListToscaDataDefinition();
294
295         for(OperationInputDefinition inputDefinition : inputListToscaDataDefinition ) {
296             if(!validateInputExistsInComponent(inputDefinition, inputs)) {
297                 String missingPropertyName = inputDefinition.getInputId().contains(".") ? inputDefinition.getInputId().substring(inputDefinition.getInputId().indexOf(".") + 1) : inputDefinition.getInputId();
298                 LOGGER.error("Interface operation input property {} not found in component input properties", missingPropertyName);
299                 ResponseFormat inputResponse = responseFormatManager.getResponseFormat(ActionStatus.INTERFACE_OPERATION_INPUT_PROPERTY_NOT_FOUND_IN_COMPONENT, missingPropertyName);
300                 return Either.right(inputResponse);
301             }
302         }
303         return Either.left(Boolean.TRUE);
304     }
305
306     private boolean validateInputExistsInComponent(OperationInputDefinition input,
307                                                    List<InputDefinition> inputs) {
308         return inputs.stream().anyMatch(inp -> inp.getUniqueId().equals(input.getInputId()));
309     }
310
311     private ResponseFormatManager getResponseFormatManager() {
312         return ResponseFormatManager.getInstance();
313     }
314
315 }