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