Allign properties import during service import
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / property / DefaultPropertyDeclarator.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.property;
21
22 import static org.openecomp.sdc.common.api.Constants.GET_INPUT;
23 import static org.openecomp.sdc.common.api.Constants.GET_POLICY;
24
25 import com.google.gson.Gson;
26 import fj.data.Either;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.Optional;
36 import java.util.Set;
37 import java.util.stream.Collectors;
38 import org.apache.commons.collections.CollectionUtils;
39 import org.apache.commons.collections.MapUtils;
40 import org.apache.commons.lang3.StringUtils;
41 import org.json.simple.JSONObject;
42 import org.openecomp.sdc.be.components.utils.PropertiesUtils;
43 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
44 import org.openecomp.sdc.be.datatypes.elements.GetInputValueDataDefinition;
45 import org.openecomp.sdc.be.datatypes.elements.GetPolicyValueDataDefinition;
46 import org.openecomp.sdc.be.datatypes.elements.PropertiesOwner;
47 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
48 import org.openecomp.sdc.be.impl.ComponentsUtils;
49 import org.openecomp.sdc.be.model.CapabilityDefinition;
50 import org.openecomp.sdc.be.model.Component;
51 import org.openecomp.sdc.be.model.ComponentInstancePropInput;
52 import org.openecomp.sdc.be.model.IComponentInstanceConnectedElement;
53 import org.openecomp.sdc.be.model.InputDefinition;
54 import org.openecomp.sdc.be.model.PolicyDefinition;
55 import org.openecomp.sdc.be.model.PropertyDefinition;
56 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
57 import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
58 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
59 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
60 import org.openecomp.sdc.common.log.wrappers.Logger;
61 import org.openecomp.sdc.exception.ResponseFormat;
62 import org.yaml.snakeyaml.Yaml;
63
64 public abstract class DefaultPropertyDeclarator<PROPERTYOWNER extends PropertiesOwner, PROPERTYTYPE extends PropertyDataDefinition> implements
65     PropertyDeclarator {
66
67     private static final Logger log = Logger.getLogger(DefaultPropertyDeclarator.class);
68     private static final short LOOP_PROTECTION_LEVEL = 10;
69     private static final String UNDERSCORE = "_";
70     private static final String GET_INPUT_INDEX = "INDEX";
71     private final Gson gson = new Gson();
72     private ComponentsUtils componentsUtils;
73     private PropertyOperation propertyOperation;
74
75     protected DefaultPropertyDeclarator(ComponentsUtils componentsUtils, PropertyOperation propertyOperation) {
76         this.componentsUtils = componentsUtils;
77         this.propertyOperation = propertyOperation;
78     }
79
80     @Override
81     public Either<List<InputDefinition>, StorageOperationStatus> declarePropertiesAsInputs(Component component, String propertiesOwnerId,
82                                                                                            List<ComponentInstancePropInput> propsToDeclare) {
83         log.debug("#declarePropertiesAsInputs - declaring properties as inputs for component {} from properties owner {}", component.getUniqueId(),
84             propertiesOwnerId);
85         return resolvePropertiesOwner(component, propertiesOwnerId)
86             .map(propertyOwner -> declarePropertiesAsInputs(component, propertyOwner, propsToDeclare))
87             .orElse(Either.right(onPropertiesOwnerNotFound(component.getUniqueId(), propertiesOwnerId)));
88     }
89
90     protected abstract PROPERTYTYPE createDeclaredProperty(PropertyDataDefinition prop);
91
92     protected abstract Either<?, StorageOperationStatus> updatePropertiesValues(Component component, String propertiesOwnerId,
93                                                                                 List<PROPERTYTYPE> properties);
94
95     protected abstract Optional<PROPERTYOWNER> resolvePropertiesOwner(Component component, String propertiesOwnerId);
96
97     protected abstract void addPropertiesListToInput(PROPERTYTYPE declaredProp, InputDefinition input);
98
99     @Override
100     public Either<List<PolicyDefinition>, StorageOperationStatus> declarePropertiesAsPolicies(Component component, String propertiesOwnerId,
101                                                                                               List<ComponentInstancePropInput> propsToDeclare) {
102         log.debug("#declarePropertiesAsPolicies - declaring properties as policies for component {} from properties owner {}",
103             component.getUniqueId(), propertiesOwnerId);
104         return resolvePropertiesOwner(component, propertiesOwnerId)
105             .map(propertyOwner -> declarePropertiesAsPolicies(component, propertyOwner, propsToDeclare))
106             .orElse(Either.right(onPropertiesOwnerNotFound(component.getUniqueId(), propertiesOwnerId)));
107     }
108
109     @Override
110     public Either<InputDefinition, StorageOperationStatus> declarePropertiesAsListInput(Component component, String propertiesOwnerId,
111                                                                                         List<ComponentInstancePropInput> propsToDeclare,
112                                                                                         InputDefinition input) {
113         log.debug("#declarePropertiesAsListInput - declaring properties as inputs for component {} from properties owner {}", component.getUniqueId(),
114             propertiesOwnerId);
115         Optional<PROPERTYOWNER> propertyOwner = resolvePropertiesOwner(component, propertiesOwnerId);
116         if (propertyOwner.isPresent()) {
117             return declarePropertiesAsListInput(component, propertyOwner.get(), propsToDeclare, input);
118         } else {
119             return Either.right(onPropertiesOwnerNotFound(component.getUniqueId(), propertiesOwnerId));
120         }
121     }
122
123     public StorageOperationStatus unDeclarePropertiesAsPolicies(Component component, PolicyDefinition policy) {
124         return StorageOperationStatus.OK;
125     }
126
127     private Either<List<PolicyDefinition>, StorageOperationStatus> declarePropertiesAsPolicies(Component component, PROPERTYOWNER propertiesOwner,
128                                                                                                List<ComponentInstancePropInput> propsToDeclare) {
129         PropertiesDeclarationData policyProperties = createPoliciesAndOverridePropertiesValues(propertiesOwner.getUniqueId(), propertiesOwner,
130             propsToDeclare);
131         return updatePropertiesValues(component, propertiesOwner.getUniqueId(), policyProperties.getPropertiesToUpdate()).left()
132             .map(updatePropsRes -> policyProperties.getPoliciesToCreate());
133     }
134
135     private StorageOperationStatus onPropertiesOwnerNotFound(String componentId, String propertiesOwnerId) {
136         log.debug("#declarePropertiesAsInputs - properties owner {} was not found on component {}", propertiesOwnerId, componentId);
137         return StorageOperationStatus.NOT_FOUND;
138     }
139
140     private Either<List<InputDefinition>, StorageOperationStatus> declarePropertiesAsInputs(Component component, PROPERTYOWNER propertiesOwner,
141                                                                                             List<ComponentInstancePropInput> propsToDeclare) {
142         PropertiesDeclarationData inputsProperties = createInputsAndOverridePropertiesValues(component, propertiesOwner, propsToDeclare);
143         return updatePropertiesValues(component, propertiesOwner.getUniqueId(), inputsProperties.getPropertiesToUpdate()).left()
144             .map(updatePropsRes -> inputsProperties.getInputsToCreate());
145     }
146
147     private PropertiesDeclarationData createPoliciesAndOverridePropertiesValues(String componentId, PROPERTYOWNER propertiesOwner,
148                                                                                 List<ComponentInstancePropInput> propsToDeclare) {
149         List<PROPERTYTYPE> declaredProperties = new ArrayList<>();
150         List<PolicyDefinition> policies = new ArrayList<>();
151         propsToDeclare.forEach(property -> policies.add(declarePropertyPolicy(componentId, declaredProperties, property)));
152         return new PropertiesDeclarationData(null, policies, declaredProperties);
153     }
154
155     private PolicyDefinition declarePropertyPolicy(String componentId, List<PROPERTYTYPE> declaredProperties, ComponentInstancePropInput propInput) {
156         PropertyDataDefinition prop = resolveProperty(declaredProperties, propInput);
157         propInput.setOwnerId(null);
158         propInput.setParentUniqueId(null);
159         PolicyDefinition policyDefinition = new PolicyDefinition(prop);
160         policyDefinition.setUniqueId(UniqueIdBuilder.buildPolicyUniqueId(componentId, prop.getName()));
161         policyDefinition.setInputPath(prop.getName());
162         policyDefinition.setInstanceUniqueId(componentId);
163         policyDefinition.setPropertyId(prop.getUniqueId());
164         changePropertyValueToGetPolicy(prop, policyDefinition);
165         PROPERTYTYPE declaredProperty = createDeclaredProperty(prop);
166         if (!declaredProperties.contains(declaredProperty)) {
167             declaredProperties.add(declaredProperty);
168         }
169         return policyDefinition;
170     }
171
172     private void changePropertyValueToGetPolicy(PropertyDataDefinition prop, PolicyDefinition policyDefinition) {
173         JSONObject jsonObject = new JSONObject();
174         String origValue = Objects.isNull(prop.getValue()) ? prop.getDefaultValue() : prop.getValue();
175         jsonObject.put(GET_POLICY, null);
176         prop.setValue(jsonObject.toJSONString());
177         policyDefinition.setValue(jsonObject.toJSONString());
178         if (CollectionUtils.isEmpty(prop.getGetPolicyValues())) {
179             prop.setGetPolicyValues(new ArrayList<>());
180         }
181         List<GetPolicyValueDataDefinition> getPolicyValues = prop.getGetPolicyValues();
182         GetPolicyValueDataDefinition getPolicyValueDataDefinition = new GetPolicyValueDataDefinition();
183         getPolicyValueDataDefinition.setPolicyId(policyDefinition.getUniqueId());
184         getPolicyValueDataDefinition.setPropertyName(prop.getName());
185         getPolicyValueDataDefinition.setOrigPropertyValue(origValue);
186         getPolicyValues.add(getPolicyValueDataDefinition);
187         policyDefinition.setGetPolicyValues(getPolicyValues);
188     }
189
190     private Either<InputDefinition, StorageOperationStatus> declarePropertiesAsListInput(Component component, PROPERTYOWNER propertiesOwner,
191                                                                                          List<ComponentInstancePropInput> propsToDeclare,
192                                                                                          InputDefinition input) {
193         List<PROPERTYTYPE> declaredProperties = new ArrayList<>();
194         for (ComponentInstancePropInput propInput : propsToDeclare) {
195             if (StringUtils.isNotEmpty(propInput.getPropertiesName()) && propInput.getInput() != null) {
196                 // sub-property in complex type is checked on UI. currently not supported.
197                 log.debug("skip propInput (propertiesName={}) currently not supported.", propInput.getPropertiesName());
198                 continue;
199             }
200             PROPERTYTYPE declaredProperty = createDeclaredProperty(propInput);
201             JSONObject jsonObject = new JSONObject();
202             jsonObject.put(GET_INPUT, Arrays.asList(input.getName(), GET_INPUT_INDEX, propInput.getName()));
203             declaredProperty.setValue(jsonObject.toJSONString());
204             GetInputValueDataDefinition getInputValueDataDefinition = new GetInputValueDataDefinition();
205             getInputValueDataDefinition.setInputId(input.getUniqueId());
206             getInputValueDataDefinition.setInputName(input.getName());
207             List<GetInputValueDataDefinition> getInputValues = declaredProperty.getGetInputValues();
208             if (getInputValues == null) {
209                 getInputValues = new ArrayList<>();
210                 declaredProperty.setGetInputValues(getInputValues);
211             }
212             getInputValues.add(getInputValueDataDefinition);
213             if (!declaredProperties.contains(declaredProperty)) {
214                 // Add property to the list if not contain in declareProperties.
215                 declaredProperties.add(declaredProperty);
216             }
217         }
218         return updatePropertiesValues(component, propertiesOwner.getUniqueId(), declaredProperties).left().map(x -> input);
219     }
220
221     private PropertiesDeclarationData createInputsAndOverridePropertiesValues(Component component, PROPERTYOWNER propertiesOwner,
222                                                                               List<ComponentInstancePropInput> propsToDeclare) {
223         List<PROPERTYTYPE> declaredProperties = new ArrayList<>();
224         List<InputDefinition> createdInputs = propsToDeclare.stream()
225             .map(propInput -> declarePropertyInput(component, propertiesOwner, declaredProperties, propInput)).collect(Collectors.toList());
226         return new PropertiesDeclarationData(createdInputs, null, declaredProperties);
227     }
228
229     private InputDefinition declarePropertyInput(Component component, PROPERTYOWNER propertiesOwner, List<PROPERTYTYPE> declaredProperties,
230                                                  ComponentInstancePropInput propInput) {
231         PropertyDataDefinition prop = resolveProperty(declaredProperties, propInput);
232         InputDefinition inputDefinition = createInput(component, propertiesOwner, propInput, prop);
233         PROPERTYTYPE declaredProperty = createDeclaredProperty(prop);
234         if (!declaredProperties.contains(declaredProperty)) {
235             declaredProperties.add(declaredProperty);
236         }
237         addPropertiesListToInput(declaredProperty, inputDefinition);
238         return inputDefinition;
239     }
240
241     private InputDefinition createInput(Component component, PROPERTYOWNER propertiesOwner, ComponentInstancePropInput propInput,
242                                         PropertyDataDefinition prop) {
243         String generatedInputPrefix = propertiesOwner.getNormalizedName();
244         if (component.getUniqueId().equals(propInput.getParentUniqueId())) {
245             //Creating input from property create on self using add property..Do not add the prefix
246             generatedInputPrefix = null;
247         }
248         Optional<CapabilityDefinition> propertyCapability = PropertiesUtils
249             .getPropertyCapabilityOfChildInstance(propInput.getParentUniqueId(), component.getCapabilities());
250         if (propertyCapability.isPresent()) {
251             String capName = propertyCapability.get().getName();
252             if (capName.contains(".")) {
253                 capName = capName.replaceAll("\\.", UNDERSCORE);
254             }
255             generatedInputPrefix =
256                 generatedInputPrefix == null || generatedInputPrefix.isEmpty() ? capName : generatedInputPrefix + UNDERSCORE + capName;
257         }
258         String generatedInputName = null;
259         if (StringUtils.isNotEmpty(propInput.getInputName())) {
260             generatedInputName = propInput.getInputName();
261         } else {
262             generatedInputName = generateInputName(generatedInputPrefix, propInput);
263         }
264         log.debug("createInput: propOwner.uniqueId={}, propInput.parentUniqueId={}", propertiesOwner.getUniqueId(), propInput.getParentUniqueId());
265         return createInputFromProperty(component.getUniqueId(), propertiesOwner, generatedInputName, propInput, prop);
266     }
267
268     private String generateInputName(String inputName, ComponentInstancePropInput propInput) {
269         String declaredInputName;
270         String[] parsedPropNames = propInput.getParsedPropNames();
271         if (parsedPropNames != null) {
272             declaredInputName = handleInputName(inputName, parsedPropNames);
273         } else {
274             String[] propName = {propInput.getName()};
275             declaredInputName = handleInputName(inputName, propName);
276         }
277         return validateDeclaredInputName(declaredInputName);
278     }
279
280     private String validateDeclaredInputName(String inputName) {
281         return inputName.replace("::", "_");
282     }
283
284     private String handleInputName(String inputName, String[] parsedPropNames) {
285         StringBuilder prefix = new StringBuilder();
286         int startingIndex;
287         if (Objects.isNull(inputName)) {
288             prefix.append(parsedPropNames[0]);
289             startingIndex = 1;
290         } else {
291             prefix.append(inputName);
292             startingIndex = 0;
293         }
294         while (startingIndex < parsedPropNames.length) {
295             prefix.append(UNDERSCORE);
296             prefix.append(parsedPropNames[startingIndex]);
297             startingIndex++;
298         }
299         return prefix.toString();
300     }
301
302     private PropertyDataDefinition resolveProperty(List<PROPERTYTYPE> propertiesToCreate, ComponentInstancePropInput propInput) {
303         Optional<PROPERTYTYPE> resolvedProperty = propertiesToCreate.stream().filter(p -> p.getName().equals(propInput.getName())).findFirst();
304         return resolvedProperty.isPresent() ? resolvedProperty.get() : propInput;
305     }
306
307     InputDefinition createInputFromProperty(String componentId, PROPERTYOWNER propertiesOwner, String inputName, ComponentInstancePropInput propInput,
308                                             PropertyDataDefinition prop) {
309         String propertiesName = propInput.getPropertiesName();
310         PropertyDefinition selectedProp = propInput.getInput();
311         String[] parsedPropNames = propInput.getParsedPropNames();
312         InputDefinition input;
313         boolean complexProperty = false;
314
315         if (propertiesName != null && !propertiesName.isEmpty() && selectedProp != null) {
316             complexProperty = true;
317             input = new InputDefinition(selectedProp);
318             input.setDefaultValue(selectedProp.getValue());
319         } else {
320             input = new InputDefinition(prop);
321             input.setDefaultValue(prop.getValue());
322         }
323
324         input.setName(inputName);
325         input.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(componentId, input.getName()));
326         input.setInputPath(propertiesName);
327         input.setInstanceUniqueId(propertiesOwner.getUniqueId());
328         input.setPropertyId(propInput.getUniqueId());
329
330         if (Objects.isNull(input.getSubPropertyInputPath()) || (propertiesName != null && input.getSubPropertyInputPath()
331             .substring(input.getSubPropertyInputPath().lastIndexOf('#')).equals(propertiesName.substring(propertiesName.lastIndexOf('#'))))) {
332             input.setParentPropertyType(propInput.getType());
333             input.setSubPropertyInputPath(propertiesName);
334         }
335
336         changePropertyValueToGetInputValue(inputName, parsedPropNames, input, prop, complexProperty);
337
338         if (prop instanceof IComponentInstanceConnectedElement) {
339             ((IComponentInstanceConnectedElement) prop).setComponentInstanceId(propertiesOwner.getUniqueId());
340             ((IComponentInstanceConnectedElement) prop).setComponentInstanceName(propertiesOwner.getName());
341         }
342
343         return input;
344     }
345
346     private void changePropertyValueToGetInputValue(String inputName, String[] parsedPropNames, InputDefinition input, PropertyDataDefinition prop,
347                                                     boolean complexProperty) {
348         JSONObject jsonObject = new JSONObject();
349         String value = prop.getValue();
350         if (value == null || value.isEmpty()) {
351             if (complexProperty) {
352                 jsonObject = createJSONValueForProperty(parsedPropNames.length - 1, parsedPropNames, jsonObject, inputName);
353                 prop.setValue(jsonObject.toJSONString());
354             } else {
355                 jsonObject.put(GET_INPUT, input.getName());
356                 prop.setValue(jsonObject.toJSONString());
357             }
358         } else {
359             Object objValue = new Yaml().load(value);
360             if (objValue instanceof Map || objValue instanceof List) {
361                 if (!complexProperty) {
362                     jsonObject.put(GET_INPUT, input.getName());
363                     prop.setValue(jsonObject.toJSONString());
364                 } else {
365                     Map<String, Object> mappedToscaTemplate = (Map<String, Object>) objValue;
366                     createInputValue(mappedToscaTemplate, 1, parsedPropNames, inputName);
367                     String json = gson.toJson(mappedToscaTemplate);
368                     prop.setValue(json);
369                 }
370             } else {
371                 jsonObject.put(GET_INPUT, input.getName());
372                 prop.setValue(jsonObject.toJSONString());
373             }
374         }
375         if (CollectionUtils.isEmpty(prop.getGetInputValues())) {
376             prop.setGetInputValues(new ArrayList<>());
377         }
378         List<GetInputValueDataDefinition> getInputValues = prop.getGetInputValues();
379         GetInputValueDataDefinition getInputValueDataDefinition = new GetInputValueDataDefinition();
380         getInputValueDataDefinition.setInputId(input.getUniqueId());
381         getInputValueDataDefinition.setInputName(input.getName());
382         getInputValues.add(getInputValueDataDefinition);
383     }
384
385     private JSONObject createJSONValueForProperty(int i, String[] parsedPropNames, JSONObject ooj, String inputName) {
386         while (i >= 1) {
387             if (i == parsedPropNames.length - 1) {
388                 JSONObject jobProp = new JSONObject();
389                 jobProp.put(GET_INPUT, inputName);
390                 ooj.put(parsedPropNames[i], jobProp);
391                 i--;
392                 return createJSONValueForProperty(i, parsedPropNames, ooj, inputName);
393             } else {
394                 JSONObject res = new JSONObject();
395                 res.put(parsedPropNames[i], ooj);
396                 i--;
397                 res = createJSONValueForProperty(i, parsedPropNames, res, inputName);
398                 return res;
399             }
400         }
401         return ooj;
402     }
403
404     private Map<String, Object> createInputValue(Map<String, Object> lhm1, int index, String[] inputNames, String inputName) {
405         while (index < inputNames.length) {
406             if (lhm1.containsKey(inputNames[index])) {
407                 Object value = lhm1.get(inputNames[index]);
408                 if (value instanceof Map) {
409                     if (index == inputNames.length - 1) {
410                         ((Map) value).put(GET_INPUT, inputName);
411                         return (Map) value;
412                     } else {
413                         index++;
414                         return createInputValue((Map) value, index, inputNames, inputName);
415                     }
416                 } else {
417                     Map<String, Object> jobProp = new HashMap<>();
418                     if (index == inputNames.length - 1) {
419                         jobProp.put(GET_INPUT, inputName);
420                         lhm1.put(inputNames[index], jobProp);
421                         return lhm1;
422                     } else {
423                         lhm1.put(inputNames[index], jobProp);
424                         index++;
425                         return createInputValue(jobProp, index, inputNames, inputName);
426                     }
427                 }
428             } else {
429                 Map<String, Object> jobProp = new HashMap<>();
430                 lhm1.put(inputNames[index], jobProp);
431                 if (index == inputNames.length - 1) {
432                     jobProp.put(GET_INPUT, inputName);
433                     return jobProp;
434                 } else {
435                     index++;
436                     return createInputValue(jobProp, index, inputNames, inputName);
437                 }
438             }
439         }
440         return lhm1;
441     }
442
443     Either<InputDefinition, ResponseFormat> prepareValueBeforeDelete(InputDefinition inputForDelete, PropertyDataDefinition inputValue,
444                                                                      List<String> pathOfComponentInstances) {
445         Either<InputDefinition, ResponseFormat> deleteEither = prepareValueBeforeDelete(inputForDelete, inputValue);
446         Either<String, JanusGraphOperationStatus> findDefaultValue = propertyOperation
447             .findDefaultValueFromSecondPosition(pathOfComponentInstances, inputValue.getUniqueId(), (String) inputValue.getDefaultValue());
448         if (findDefaultValue.isRight()) {
449             deleteEither = Either.right(componentsUtils.getResponseFormat(componentsUtils
450                 .convertFromStorageResponse(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(findDefaultValue.right().value()))));
451             return deleteEither;
452         }
453         String defaultValue = findDefaultValue.left().value();
454         inputValue.setDefaultValue(defaultValue);
455         log.debug("The returned default value in ResourceInstanceProperty is {}", defaultValue);
456         return deleteEither;
457     }
458
459     Either<InputDefinition, ResponseFormat> prepareValueBeforeDeleteOfCapProp(InputDefinition inputForDelete, PropertyDataDefinition inputValue) {
460         Either<InputDefinition, ResponseFormat> deleteEither = prepareValueBeforeDelete(inputForDelete, inputValue);
461         inputValue.setDefaultValue(inputForDelete.getDefaultValue());
462         log.debug("The returned default value in ResourceInstanceProperty is {}", inputForDelete.getDefaultValue());
463         return deleteEither;
464     }
465
466     private Either<InputDefinition, ResponseFormat> prepareValueBeforeDelete(InputDefinition inputForDelete, PropertyDataDefinition inputValue) {
467         Either<InputDefinition, ResponseFormat> deleteEither = Either.left(inputForDelete);
468         String value = inputValue.getValue();
469         Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(value);
470         resetInputName(mappedToscaTemplate, inputForDelete.getName());
471         value = "";
472         if (!mappedToscaTemplate.isEmpty()) {
473             Either result = cleanNestedMap(mappedToscaTemplate, true);
474             Map modifiedMappedToscaTemplate = mappedToscaTemplate;
475             if (result.isLeft()) {
476                 modifiedMappedToscaTemplate = (Map) result.left().value();
477             } else {
478                 log.warn("Map cleanup failed -> " + result.right().value()
479                     .toString());    //continue, don't break operation
480             }
481             value = gson.toJson(modifiedMappedToscaTemplate);
482         }
483         inputValue.setValue(value);
484         List<GetInputValueDataDefinition> getInputsValues = inputValue.getGetInputValues();
485         if (getInputsValues != null && !getInputsValues.isEmpty()) {
486             Optional<GetInputValueDataDefinition> op = getInputsValues.stream().filter(gi -> gi.getInputId().equals(inputForDelete.getUniqueId()))
487                 .findAny();
488             op.ifPresent(getInputsValues::remove);
489         }
490         inputValue.setGetInputValues(getInputsValues);
491         if (CollectionUtils.isEmpty(inputValue.getGetInputValues()) && inputValue.getToscaFunction() != null) {
492             inputValue.setToscaFunction(null);
493         }
494         return deleteEither;
495     }
496
497     private void resetInputName(Map<String, Object> lhm1, String inputName) {
498         for (Map.Entry<String, Object> entry : lhm1.entrySet()) {
499             String key = entry.getKey();
500             Object value = entry.getValue();
501             if (value instanceof String && ((String) value).equalsIgnoreCase(inputName) && key.equals(GET_INPUT)) {
502                 lhm1.remove(key);
503             } else if (value instanceof Map) {
504                 Map<String, Object> subMap = (Map<String, Object>) value;
505                 resetInputName(subMap, inputName);
506             } else if (value instanceof List && ((List) value).contains(inputName) && key.equals(GET_INPUT)) {
507                 lhm1.remove(key);
508             }
509         }
510     }
511
512     private Either cleanNestedMap(Map mappedToscaTemplate, boolean deepClone) {
513         if (MapUtils.isNotEmpty(mappedToscaTemplate)) {
514             if (deepClone) {
515                 if (!(mappedToscaTemplate instanceof HashMap)) {
516                     return Either.right("expecting mappedToscaTemplate as HashMap ,recieved " + mappedToscaTemplate.getClass().getSimpleName());
517                 } else {
518                     mappedToscaTemplate = (HashMap) ((HashMap) mappedToscaTemplate).clone();
519                 }
520             }
521             return Either.left((Map) cleanEmptyNestedValuesInMap(mappedToscaTemplate, LOOP_PROTECTION_LEVEL));
522         } else {
523             log.debug("mappedToscaTemplate is empty ");
524             return Either.right("mappedToscaTemplate is empty ");
525         }
526     }
527
528     /*        Mutates the object
529      *        Tail recurse -> traverse the tosca elements and remove nested empty map properties
530      *        this only handles nested maps, other objects are left untouched (even a Set containing a map) since behaviour is unexpected
531      *
532      *        @param  toscaElement - expected map of tosca values
533      *        @return mutated @param toscaElement , where empty maps are deleted , return null for empty map.
534      **/
535     private Object cleanEmptyNestedValuesInMap(Object toscaElement, short loopProtectionLevel) {
536         if (loopProtectionLevel <= 0 || toscaElement == null || !(toscaElement instanceof Map)) {
537             return toscaElement;
538         }
539         if (MapUtils.isNotEmpty((Map) toscaElement)) {
540             Object ret;
541             Set<Object> keysToRemove = new HashSet<>();                                                                 // use different set to avoid ConcurrentModificationException
542             for (Object key : ((Map) toscaElement).keySet()) {
543                 Object value = ((Map) toscaElement).get(key);
544                 ret = cleanEmptyNestedValuesInMap(value, --loopProtectionLevel);
545                 if (ret == null) {
546                     keysToRemove.add(key);
547                 }
548             }
549             Collection set = ((Map) toscaElement).keySet();
550             if (CollectionUtils.isNotEmpty(set)) {
551                 set.removeAll(keysToRemove);
552             }
553             if (isEmptyNestedMap(toscaElement)) {
554                 return null;
555             }
556         } else {
557             return null;
558         }
559         return toscaElement;
560     }
561
562     //ignores other collection objects
563     private boolean isEmptyNestedMap(Object element) {
564         boolean isEmpty = true;
565         if (element != null) {
566             if (element instanceof Map) {
567                 if (MapUtils.isEmpty((Map) element)) {
568                     isEmpty = true;
569                 } else {
570                     for (Object key : ((Map) (element)).keySet()) {
571                         Object value = ((Map) (element)).get(key);
572                         isEmpty &= isEmptyNestedMap(value);
573                     }
574                 }
575             } else {
576                 isEmpty = false;
577             }
578         }
579         return isEmpty;
580     }
581     //@returns true iff map nested maps are all empty
582
583     private class PropertiesDeclarationData {
584
585         private List<InputDefinition> inputsToCreate;
586         private List<PolicyDefinition> policiesToCreate;
587         private List<PROPERTYTYPE> propertiesToUpdate;
588
589         PropertiesDeclarationData(List<InputDefinition> inputsToCreate, List<PolicyDefinition> policiesToCreate,
590                                   List<PROPERTYTYPE> propertiesToUpdate) {
591             this.inputsToCreate = inputsToCreate;
592             this.policiesToCreate = policiesToCreate;
593             this.propertiesToUpdate = propertiesToUpdate;
594         }
595
596         List<InputDefinition> getInputsToCreate() {
597             return inputsToCreate;
598         }
599
600         public List<PolicyDefinition> getPoliciesToCreate() {
601             return policiesToCreate;
602         }
603
604         List<PROPERTYTYPE> getPropertiesToUpdate() {
605             return propertiesToUpdate;
606         }
607     }
608 }