Fix not possible to edit property with custom tosca function
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / ImportUtils.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 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.impl;
22
23 import static org.apache.commons.collections.CollectionUtils.isEmpty;
24 import static org.openecomp.sdc.be.components.impl.ResourceImportManager.PROPERTY_NAME_PATTERN_IGNORE_LENGTH;
25 import static org.openecomp.sdc.be.datatypes.elements.Annotation.setAnnotationsName;
26
27 import com.google.gson.Gson;
28 import com.google.gson.GsonBuilder;
29 import com.google.gson.JsonParseException;
30 import com.google.gson.reflect.TypeToken;
31 import fj.data.Either;
32 import java.lang.reflect.Type;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.Iterator;
38 import java.util.LinkedHashMap;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Map.Entry;
42 import java.util.function.Consumer;
43 import java.util.function.Function;
44 import lombok.AccessLevel;
45 import lombok.NoArgsConstructor;
46 import org.apache.commons.collections.CollectionUtils;
47 import org.apache.commons.collections4.MapUtils;
48 import org.apache.commons.text.StringEscapeUtils;
49 import org.onap.sdc.tosca.datatypes.model.EntrySchema;
50 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
51 import org.openecomp.sdc.be.config.BeEcompErrorManager;
52 import org.openecomp.sdc.be.dao.api.ActionStatus;
53 import org.openecomp.sdc.be.datatypes.elements.Annotation;
54 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
55 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
56 import org.openecomp.sdc.be.datatypes.enums.ConstraintType;
57 import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields;
58 import org.openecomp.sdc.be.impl.ComponentsUtils;
59 import org.openecomp.sdc.be.model.AnnotationTypeDefinition;
60 import org.openecomp.sdc.be.model.AttributeDefinition;
61 import org.openecomp.sdc.be.model.HeatParameterDefinition;
62 import org.openecomp.sdc.be.model.InputDefinition;
63 import org.openecomp.sdc.be.model.LifecycleStateEnum;
64 import org.openecomp.sdc.be.model.OutputDefinition;
65 import org.openecomp.sdc.be.model.PropertyConstraint;
66 import org.openecomp.sdc.be.model.PropertyDefinition;
67 import org.openecomp.sdc.be.model.heat.HeatParameterType;
68 import org.openecomp.sdc.be.model.operations.impl.AnnotationTypeOperations;
69 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser;
70 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
71 import org.openecomp.sdc.be.model.tosca.constraints.AbstractComparablePropertyConstraint;
72 import org.openecomp.sdc.be.model.tosca.constraints.InRangeConstraint;
73 import org.openecomp.sdc.be.model.tosca.constraints.ValidValuesConstraint;
74 import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
75 import org.openecomp.sdc.be.utils.TypeUtils;
76 import org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum;
77 import org.openecomp.sdc.common.api.ArtifactTypeEnum;
78 import org.openecomp.sdc.common.log.wrappers.Logger;
79 import org.openecomp.sdc.exception.ResponseFormat;
80 import org.springframework.beans.factory.annotation.Autowired;
81 import org.springframework.beans.factory.config.YamlProcessor;
82 import org.springframework.stereotype.Component;
83 import org.yaml.snakeyaml.DumperOptions;
84 import org.yaml.snakeyaml.Yaml;
85 import org.yaml.snakeyaml.constructor.Constructor;
86 import org.yaml.snakeyaml.nodes.Tag;
87 import org.yaml.snakeyaml.representer.Representer;
88 import org.yaml.snakeyaml.resolver.Resolver;
89
90 @NoArgsConstructor(access = AccessLevel.PRIVATE)
91 @Component
92 public final class ImportUtils {
93
94     private static final CustomResolver customResolver = new CustomResolver();
95     private static final Yaml strictYamlLoader = new YamlLoader().getStrictYamlLoader();
96     private static final Logger log = Logger.getLogger(ImportUtils.class);
97     private static ComponentsUtils componentsUtils;
98
99     @Autowired
100     public static void setComponentsUtils(ComponentsUtils cu) {
101         componentsUtils = cu;
102     }
103
104     private static void buildMap(Map<String, Object> output, Map<String, Object> map) {
105         for (Entry<String, Object> entry : map.entrySet()) {
106             String key = entry.getKey();
107             Object value = entry.getValue();
108             if (value instanceof Map) {
109                 Map<String, Object> result = new LinkedHashMap<>();
110                 buildMap(result, (Map) value);
111                 output.put(key, result);
112             } else if (value instanceof Collection) {
113                 Map<String, Object> result = new LinkedHashMap<>();
114                 int i = 0;
115                 for (Object item : (Collection<Object>) value) {
116                     buildMap(result, Collections.singletonMap("[" + (i++) + "]", item));
117                 }
118                 output.put(key, new ArrayList<>(result.values()));
119             } else {
120                 output.put(key, value);
121             }
122         }
123     }
124
125     public static Map<String, Object> loadYamlAsStrictMap(String content) {
126         Map<String, Object> result = new LinkedHashMap<>();
127         Object map = strictYamlLoader.load(content);
128         buildMap(result, (Map<String, Object>) map);
129         return result;
130     }
131
132     @SuppressWarnings("unchecked")
133     public static Either<List<HeatParameterDefinition>, ResultStatusEnum> getHeatParamsWithoutImplicitTypes(String heatDecodedPayload,
134                                                                                                             String artifactType) {
135         Map<String, Object> heatData = (Map<String, Object>) new Yaml(new Constructor(), new Representer(), new DumperOptions(), customResolver)
136             .load(heatDecodedPayload);
137         return getHeatParameters(heatData, artifactType);
138     }
139
140     @SuppressWarnings("unchecked")
141     private static void handleElementNameNotFound(String elementName, Object elementValue, ToscaElementTypeEnum elementType,
142                                                   List<Object> returnedList) {
143         if (elementValue instanceof Map) {
144             findToscaElements((Map<String, Object>) elementValue, elementName, elementType, returnedList);
145         } else if (elementValue instanceof List) {
146             findAllToscaElementsInList((List<Object>) elementValue, elementName, elementType, returnedList);
147         }
148     }
149
150     @SuppressWarnings("unchecked")
151     private static void addFoundElementAccordingToItsType(String elementName, ToscaElementTypeEnum elementType, List<Object> returnedList,
152                                                           Object elementValue) {
153         if (elementValue instanceof Boolean) {
154             if (elementType == ToscaElementTypeEnum.BOOLEAN || elementType == ToscaElementTypeEnum.ALL) {
155                 returnedList.add(elementValue);
156             }
157         } else if (elementValue instanceof String) {
158             if (elementType == ToscaElementTypeEnum.STRING || elementType == ToscaElementTypeEnum.ALL) {
159                 returnedList.add(elementValue);
160             }
161         } else if (elementValue instanceof Map) {
162             if (elementType == ToscaElementTypeEnum.MAP || elementType == ToscaElementTypeEnum.ALL) {
163                 returnedList.add(elementValue);
164             }
165             findToscaElements((Map<String, Object>) elementValue, elementName, elementType, returnedList);
166         } else if (elementValue instanceof List) {
167             if (elementType == ToscaElementTypeEnum.LIST || elementType == ToscaElementTypeEnum.ALL) {
168                 returnedList.add(elementValue);
169             }
170             findAllToscaElementsInList((List<Object>) elementValue, elementName, elementType, returnedList);
171         }
172         // For Integer, Double etc...
173         else if (elementType == ToscaElementTypeEnum.ALL && elementValue != null) {
174             returnedList.add(String.valueOf(elementValue));
175         }
176     }
177
178     private static void findAllToscaElementsInList(List<Object> list, String elementName, ToscaElementTypeEnum elementType,
179                                                    List<Object> returnedList) {
180         list.forEach(elementValue -> handleElementNameNotFound(elementName, elementValue, elementType, returnedList));
181     }
182
183     public static Either<Object, ResultStatusEnum> findToscaElement(Map<String, Object> toscaJson, TypeUtils.ToscaTagNamesEnum elementName,
184                                                                     ToscaElementTypeEnum elementType) {
185         final var toscaElements = findToscaElements(toscaJson, elementName.getElementName(), elementType, new ArrayList<>());
186         if (toscaElements.isLeft() && CollectionUtils.isNotEmpty(toscaElements.left().value())) {
187             return Either.left(toscaElements.left().value().get(0));
188         }
189         return Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
190     }
191
192     /**
193      * Recursively searches for all tosca elements with key equals to elementName and value equals to elementType. <br> Returns Either element
194      * with:<br> List with all value if values found<br> Or ELEMENT_NOT_FOUND ActionStatus
195      *
196      * @param toscaJson
197      * @return
198      */
199     public static Either<List<Object>, ResultStatusEnum> findToscaElements(Map<String, Object> toscaJson, String elementName,
200                                                                            ToscaElementTypeEnum elementType, List<Object> returnedList) {
201         Either<List<Object>, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
202         String skipKey = null;
203         if (toscaJson.containsKey(elementName)) {
204             skipKey = handleFoundElement(toscaJson, elementName, elementType, returnedList);
205         }
206         Iterator<Entry<String, Object>> keyValItr = toscaJson.entrySet().iterator();
207         while (keyValItr.hasNext()) {
208             Entry<String, Object> keyValEntry = keyValItr.next();
209             if (!String.valueOf(keyValEntry.getKey()).equals(skipKey)) {
210                 handleElementNameNotFound(elementName, keyValEntry.getValue(), elementType, returnedList);
211             }
212         }
213         if (!isEmpty(returnedList)) {
214             returnedElement = Either.left(returnedList);
215         }
216         return returnedElement;
217     }
218
219     private static String handleFoundElement(Map<String, Object> toscaJson, String elementName, ToscaElementTypeEnum elementType,
220                                              List<Object> returnedList) {
221         Object elementValue = toscaJson.get(elementName);
222         addFoundElementAccordingToItsType(elementName, elementType, returnedList, elementValue);
223         return elementName;
224     }
225
226     @SuppressWarnings("unchecked")
227     public static <T> Either<List<T>, ResultStatusEnum> findFirstToscaListElement(Map<String, Object> toscaJson,
228                                                                                   TypeUtils.ToscaTagNamesEnum toscaTagName) {
229         Either<List<T>, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
230         Either<Object, ResultStatusEnum> findFirstToscaElement = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.LIST);
231         if (findFirstToscaElement.isLeft()) {
232             returnedElement = Either.left((List<T>) findFirstToscaElement.left().value());
233         }
234         return returnedElement;
235     }
236
237     @SuppressWarnings("unchecked")
238     public static <T> Either<Map<String, T>, ResultStatusEnum> findFirstToscaMapElement(Map<String, Object> toscaJson,
239                                                                                         TypeUtils.ToscaTagNamesEnum toscaTagName) {
240         Either<Map<String, T>, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
241         Either<Object, ResultStatusEnum> findFirstToscaElement = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.MAP);
242         if (findFirstToscaElement.isLeft()) {
243             returnedElement = Either.left((Map<String, T>) findFirstToscaElement.left().value());
244         }
245         return returnedElement;
246     }
247
248     public static Either<String, ResultStatusEnum> findFirstToscaStringElement(Map<String, Object> toscaJson,
249                                                                                TypeUtils.ToscaTagNamesEnum toscaTagName) {
250         Either<String, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
251         Either<Object, ResultStatusEnum> findFirstToscaElements = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.STRING);
252         if (findFirstToscaElements.isLeft()) {
253             returnedElement = Either.left((String) findFirstToscaElements.left().value());
254         }
255         return returnedElement;
256     }
257
258     /**
259      * searches for first Tosca in Json map (toscaJson) boolean element by name (toscaTagName) returns found element or ELEMENT_NOT_FOUND status
260      *
261      * @param toscaJson
262      * @param toscaTagName
263      * @return
264      */
265     private static Either<String, ResultStatusEnum> findFirstToscaBooleanElement(Map<String, Object> toscaJson,
266                                                                                  TypeUtils.ToscaTagNamesEnum toscaTagName) {
267         Either<String, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
268         Either<Object, ResultStatusEnum> findFirstToscaElements = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.BOOLEAN);
269         if (findFirstToscaElements.isLeft()) {
270             returnedElement = Either.left(String.valueOf(findFirstToscaElements.left().value()));
271         }
272         return returnedElement;
273     }
274
275     private static void setPropertyConstraints(Map<String, Object> propertyValue, PropertyDefinition property) {
276         List<PropertyConstraint> constraints = getPropertyConstraints(propertyValue, property.getType(), property.getSchema());
277         if (CollectionUtils.isNotEmpty(constraints)) {
278             property.setConstraints(constraints);
279         }
280     }
281
282     private static List<PropertyConstraint> getPropertyConstraints(final Map<String, Object> propertyValue, final String propertyType,
283                                                                    final SchemaDefinition schema) {
284         final List<Object> propertyFieldConstraints = findCurrentLevelConstraintsElement(propertyValue);
285         if (CollectionUtils.isEmpty(propertyFieldConstraints)) {
286             return Collections.emptyList();
287         }
288         final List<PropertyConstraint> constraintList = new ArrayList<>();
289         final Type constraintType = new TypeToken<PropertyConstraint>() {
290         }.getType();
291         final Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
292         for (final Object constraintJson : propertyFieldConstraints) {
293             final PropertyConstraint propertyConstraint =
294                 validateAndGetPropertyConstraint(propertyType, constraintType, gson, constraintJson, schema);
295             if (propertyConstraint != null) {
296                 constraintList.add(propertyConstraint);
297             }
298         }
299         return constraintList;
300     }
301
302     private static List<Object> findCurrentLevelConstraintsElement(Map<String, Object> toscaJson) {
303         List<Object> constraints = null;
304         if (toscaJson.containsKey(TypeUtils.ToscaTagNamesEnum.CONSTRAINTS.getElementName())) {
305             try {
306                 constraints = (List<Object>) toscaJson.get(TypeUtils.ToscaTagNamesEnum.CONSTRAINTS.getElementName());
307             } catch (ClassCastException e) {
308                 throw new ByActionStatusComponentException(ActionStatus.INVALID_PROPERTY_CONSTRAINTS_FORMAT,
309                     toscaJson.get(TypeUtils.ToscaTagNamesEnum.CONSTRAINTS.getElementName()).toString());
310             }
311         }
312         return constraints;
313     }
314
315     private static PropertyConstraint validateAndGetPropertyConstraint(String propertyType, Type constraintType, Gson gson, Object constraintJson,
316                                                                        SchemaDefinition schema) {
317         PropertyConstraint propertyConstraint;
318         try {
319             propertyConstraint = gson.fromJson(gson.toJson(constraintJson), constraintType);
320         } catch (ClassCastException | JsonParseException e) {
321             throw new ByActionStatusComponentException(ActionStatus.INVALID_PROPERTY_CONSTRAINTS_FORMAT, constraintJson.toString());
322         }
323         if (propertyConstraint instanceof ValidValuesConstraint) {
324             try {
325                 ((ValidValuesConstraint) propertyConstraint).validateType(propertyType, schema);
326                 boolean valid = ((ValidValuesConstraint) propertyConstraint).validateValueType(propertyType);
327                 if (!valid) {
328                     ((ValidValuesConstraint) propertyConstraint).changeConstraintValueTypeTo(propertyType);
329                 }
330             } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
331                 BeEcompErrorManager.getInstance()
332                     .logInternalFlowError("GetInitializedPropertyConstraint", e.getMessage(), BeEcompErrorManager.ErrorSeverity.ERROR);
333                 throw new ByActionStatusComponentException(ActionStatus.INVALID_PROPERTY_CONSTRAINTS, ConstraintType.VALID_VALUES.name(),
334                     ((ValidValuesConstraint) propertyConstraint).getValidValues().toString(), propertyType);
335             }
336         } else if (propertyConstraint instanceof AbstractComparablePropertyConstraint) {
337             try {
338                 boolean valid = ((AbstractComparablePropertyConstraint) propertyConstraint).validateValueType(propertyType);
339                 if (!valid) {
340                     ((AbstractComparablePropertyConstraint) propertyConstraint).changeConstraintValueTypeTo(propertyType);
341                 }
342             } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
343                 throw new ByActionStatusComponentException(ActionStatus.INVALID_PROPERTY_CONSTRAINTS, propertyConstraint.getConstraintType().name(),
344                     propertyConstraint.toString(), propertyType);
345             }
346         } else if (propertyConstraint instanceof InRangeConstraint) {
347             try {
348                 boolean valid = ((InRangeConstraint) propertyConstraint).validateValueType(propertyType);
349                 if (!valid) {
350                     ((InRangeConstraint) propertyConstraint).changeConstraintValueTypeTo(propertyType);
351                 }
352             } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
353                 throw new ByActionStatusComponentException(ActionStatus.INVALID_PROPERTY_CONSTRAINTS, ConstraintType.IN_RANGE.name(),
354                     String.valueOf(((InRangeConstraint) propertyConstraint).getInRange()), propertyType);
355             }
356         }
357         return propertyConstraint;
358     }
359
360     static PropertyDefinition createModuleProperty(Map<String, Object> propertyValue) {
361         PropertyDefinition propertyDef = new PropertyDefinition();
362         setField(propertyValue, TypeUtils.ToscaTagNamesEnum.TYPE, propertyDef::setType);
363         setFieldBoolean(propertyValue, ToscaTagNamesEnum.REQUIRED, req -> propertyDef.setRequired(Boolean.parseBoolean(req)));
364         setField(propertyValue, TypeUtils.ToscaTagNamesEnum.DESCRIPTION, propertyDef::setDescription);
365         setJsonStringField(propertyValue, TypeUtils.ToscaTagNamesEnum.DEFAULT_VALUE, propertyDef.getType(), propertyDef::setDefaultValue);
366         setJsonStringField(propertyValue, TypeUtils.ToscaTagNamesEnum.VALUE, propertyDef.getType(), propertyDef::setValue);
367         setFieldBoolean(propertyValue, TypeUtils.ToscaTagNamesEnum.IS_PASSWORD, pass -> propertyDef.setPassword(Boolean.parseBoolean(pass)));
368         setField(propertyValue, TypeUtils.ToscaTagNamesEnum.STATUS, propertyDef::setStatus);
369         setFieldMap(propertyValue, ToscaTagNamesEnum.METADATA, propertyDef::setMetadata);
370         setSchema(propertyValue, propertyDef);
371         setPropertyConstraints(propertyValue, propertyDef);
372         return propertyDef;
373     }
374
375     private static void setJsonStringField(Map<String, Object> propertyValue, ToscaTagNamesEnum elementName, String type, Consumer<String> setter) {
376         Either<Object, ResultStatusEnum> eitherValue = findToscaElement(propertyValue, elementName, ToscaElementTypeEnum.ALL);
377         if (eitherValue.isLeft()) {
378             setter.accept(getPropertyJsonStringValue(eitherValue.left().value(), type));
379         }
380     }
381
382     private static Annotation createModuleAnnotation(Map<String, Object> annotationMap, AnnotationTypeOperations annotationTypeOperations) {
383         String parsedAnnotationType = findFirstToscaStringElement(annotationMap, TypeUtils.ToscaTagNamesEnum.TYPE).left().value();
384         AnnotationTypeDefinition annotationTypeObject = annotationTypeOperations.getLatestType(parsedAnnotationType);
385         if (annotationTypeObject != null) {
386             Annotation annotation = new Annotation();
387             setField(annotationMap, TypeUtils.ToscaTagNamesEnum.TYPE, annotation::setType);
388             setField(annotationMap, TypeUtils.ToscaTagNamesEnum.DESCRIPTION, annotation::setDescription);
389             Either<Map<String, PropertyDefinition>, ResultStatusEnum> properties = getProperties(annotationMap);
390             modifyPropertiesKeysToProperForm(properties, annotation);
391             return annotation;
392         }
393         return null;
394     }
395
396     private static Either<Boolean, ResponseFormat> modifyPropertiesKeysToProperForm(
397         Either<Map<String, PropertyDefinition>, ResultStatusEnum> properties, Annotation annotation) {
398         Either<Boolean, ResponseFormat> result = Either.left(true);
399         if (properties.isLeft()) {
400             List<PropertyDataDefinition> propertiesList = new ArrayList<>();
401             Map<String, PropertyDefinition> value = properties.left().value();
402             if (value != null) {
403                 for (Entry<String, PropertyDefinition> entry : value.entrySet()) {
404                     String name = entry.getKey();
405                     if (!PROPERTY_NAME_PATTERN_IGNORE_LENGTH.matcher(name).matches()) {
406                         log.debug("The property with invalid name {} occurred upon import resource {}. ", name, annotation.getName());
407                         result = Either.right(componentsUtils.getResponseFormat(
408                             componentsUtils.convertFromResultStatusEnum(ResultStatusEnum.INVALID_PROPERTY_NAME, JsonPresentationFields.PROPERTY)));
409                     }
410                     PropertyDefinition propertyDefinition = entry.getValue();
411                     propertyDefinition.setValue(propertyDefinition.getName());
412                     propertyDefinition.setName(name);
413                     propertiesList.add(propertyDefinition);
414                 }
415             }
416             annotation.setProperties(propertiesList);
417         } else if (properties.right().value() != ResultStatusEnum.ELEMENT_NOT_FOUND) {
418             result = Either.right(componentsUtils
419                 .getResponseFormat(componentsUtils.convertFromResultStatusEnum(properties.right().value(), JsonPresentationFields.PROPERTY)));
420         }
421         return result;
422     }
423
424     private static InputDefinition createModuleInput(final Map<String, Object> inputValue, final AnnotationTypeOperations annotationTypeOperations) {
425         return parseAnnotationsAndAddItToInput(createModuleInput(inputValue), inputValue, annotationTypeOperations);
426     }
427
428     private static InputDefinition createModuleInput(final Map<String, Object> inputValue) {
429         final InputDefinition inputDef = new InputDefinition();
430         setField(inputValue, TypeUtils.ToscaTagNamesEnum.TYPE, inputDef::setType);
431         setFieldBoolean(inputValue, ToscaTagNamesEnum.REQUIRED, req -> inputDef.setRequired(Boolean.parseBoolean(req)));
432         setField(inputValue, TypeUtils.ToscaTagNamesEnum.DESCRIPTION, inputDef::setDescription);
433         setJsonStringField(inputValue, TypeUtils.ToscaTagNamesEnum.DEFAULT_VALUE, inputDef.getType(), inputDef::setDefaultValue);
434         setFieldBoolean(inputValue, TypeUtils.ToscaTagNamesEnum.IS_PASSWORD, pass -> inputDef.setPassword(Boolean.parseBoolean(pass)));
435         setField(inputValue, TypeUtils.ToscaTagNamesEnum.STATUS, inputDef::setStatus);
436         setField(inputValue, TypeUtils.ToscaTagNamesEnum.LABEL, inputDef::setLabel);
437         setFieldBoolean(inputValue, TypeUtils.ToscaTagNamesEnum.HIDDEN, hidden -> inputDef.setHidden(Boolean.parseBoolean(hidden)));
438         setFieldBoolean(inputValue, TypeUtils.ToscaTagNamesEnum.IMMUTABLE, immutable -> inputDef.setImmutable(Boolean.parseBoolean(immutable)));
439         setFieldMap(inputValue, ToscaTagNamesEnum.METADATA, inputDef::setMetadata);
440         setSchema(inputValue, inputDef);
441         setPropertyConstraints(inputValue, inputDef);
442         return inputDef;
443     }
444
445     private static OutputDefinition createModuleOutput(final Map<String, Object> outputValue) {
446         final OutputDefinition outputDef = new OutputDefinition();
447         setField(outputValue, TypeUtils.ToscaTagNamesEnum.TYPE, outputDef::setType);
448         setField(outputValue, TypeUtils.ToscaTagNamesEnum.DESCRIPTION, outputDef::setDescription);
449         setJsonStringField(outputValue, TypeUtils.ToscaTagNamesEnum.VALUE, outputDef.getType(), outputDef::setValue);
450         setField(outputValue, TypeUtils.ToscaTagNamesEnum.STATUS, outputDef::setStatus);
451         return outputDef;
452     }
453
454     private static InputDefinition parseAnnotationsAndAddItToInput(InputDefinition inputDef, Map<String, Object> inputValue,
455                                                                    AnnotationTypeOperations annotationTypeOperations) {
456         Function<String, Annotation> elementGenByName = ImportUtils::createAnnotation;
457         Function<Map<String, Object>, Annotation> func = annotation -> createModuleAnnotation(annotation, annotationTypeOperations);
458         return getElements(inputValue, TypeUtils.ToscaTagNamesEnum.ANNOTATIONS, elementGenByName, func).left()
459             .map(annotations -> modifyInputWithAnnotations(inputDef, annotations)).left().on(err -> {
460                 log.error("Parsing annotations or adding them to the PropertyDataDefinition object failed");
461                 return inputDef;
462             });
463     }
464
465     private static InputDefinition modifyInputWithAnnotations(InputDefinition inputDef, Map<String, Annotation> annotationsMap) {
466         setAnnotationsName(annotationsMap);
467         inputDef.setAnnotationsToInput(annotationsMap.values());
468         return inputDef;
469     }
470
471     static AttributeDefinition createModuleAttribute(Map<String, Object> attributeMap) {
472         AttributeDefinition attributeDef = new AttributeDefinition();
473         setField(attributeMap, TypeUtils.ToscaTagNamesEnum.TYPE, attributeDef::setType);
474         setField(attributeMap, TypeUtils.ToscaTagNamesEnum.DESCRIPTION, attributeDef::setDescription);
475         setField(attributeMap, TypeUtils.ToscaTagNamesEnum.STATUS, attributeDef::setStatus);
476         setJsonStringField(attributeMap, TypeUtils.ToscaTagNamesEnum.DEFAULT_VALUE, attributeDef.getType(), attributeDef::set_default);
477         setEntrySchema(attributeMap, attributeDef);
478         return attributeDef;
479     }
480
481     private static void setSchema(final Map<String, Object> propertyValue, final PropertyDefinition propertyDefinition) {
482         final Either<Object, ResultStatusEnum> schemaElementRes = findEntrySchemaElement(propertyValue);
483         if (schemaElementRes.isLeft()) {
484             propertyDefinition.setSchema(getSchema(schemaElementRes.left().value()));
485         }
486     }
487
488     private static void setEntrySchema(final Map<String, Object> toscaJsonMap, final AttributeDefinition attributeDefinition) {
489         final Either<Object, ResultStatusEnum> schemaElementRes = findEntrySchemaElement(toscaJsonMap);
490         if (schemaElementRes.isLeft()) {
491             attributeDefinition.setEntry_schema(createEntrySchema(schemaElementRes.left().value()));
492         }
493     }
494
495     private static Either<Object, ResultStatusEnum> findEntrySchemaElement(final Map<String, Object> propertyValue) {
496         return findToscaElement(propertyValue, TypeUtils.ToscaTagNamesEnum.ENTRY_SCHEMA, ToscaElementTypeEnum.ALL);
497     }
498
499     private static SchemaDefinition getSchema(Object propertyFieldEntryScheme) {
500         SchemaDefinition schema = new SchemaDefinition();
501         if (propertyFieldEntryScheme instanceof String) {
502             String schemaType = (String) propertyFieldEntryScheme;
503             PropertyDefinition schemeProperty = new PropertyDefinition();
504             schemeProperty.setType(schemaType);
505             schema.setProperty(schemeProperty);
506         } else if (propertyFieldEntryScheme instanceof Map) {
507             PropertyDefinition schemeProperty = createModuleProperty((Map<String, Object>) propertyFieldEntryScheme);
508             schema.setProperty(schemeProperty);
509         }
510         return schema;
511     }
512
513     private static EntrySchema createEntrySchema(final Object toscaEntrySchemaObj) {
514         final EntrySchema entrySchema = new EntrySchema();
515         if (toscaEntrySchemaObj instanceof String) {
516             entrySchema.setType((String) toscaEntrySchemaObj);
517         } else if (toscaEntrySchemaObj instanceof Map) {
518             final PropertyDefinition schemeProperty = createModuleProperty((Map<String, Object>) toscaEntrySchemaObj);
519             entrySchema.setType(schemeProperty.getType());
520             entrySchema.setDescription(schemeProperty.getDescription());
521         }
522         return entrySchema;
523     }
524
525     private static void setField(Map<String, Object> toscaJson, TypeUtils.ToscaTagNamesEnum tagName, Consumer<String> setter) {
526         Either<String, ResultStatusEnum> fieldStringValue = findFirstToscaStringElement(toscaJson, tagName);
527         if (fieldStringValue.isLeft()) {
528             setter.accept(fieldStringValue.left().value());
529         }
530     }
531
532     private static void setFieldBoolean(Map<String, Object> toscaJson, TypeUtils.ToscaTagNamesEnum tagName, Consumer<String> setter) {
533         Either<String, ResultStatusEnum> fieldStringValue = findFirstToscaBooleanElement(toscaJson, tagName);
534         if (fieldStringValue.isLeft()) {
535             setter.accept(fieldStringValue.left().value());
536         }
537     }
538
539     private static void setFieldMap(final Map<String, Object> toscaJson, final ToscaTagNamesEnum tagName,
540                                     final Consumer<Map<String, String>> setter) {
541         final Either<Map<String, String>, ResultStatusEnum> toscaMapElement = findFirstToscaMapElement(toscaJson, tagName);
542         if (toscaMapElement.isLeft() && MapUtils.isNotEmpty(toscaMapElement.left().value())) {
543             setter.accept(toscaMapElement.left().value());
544         }
545     }
546
547     public static Either<Map<String, PropertyDefinition>, ResultStatusEnum> getProperties(Map<String, Object> toscaJson) {
548         Function<String, PropertyDefinition> elementGenByName = ImportUtils::createProperties;
549         Function<Map<String, Object>, PropertyDefinition> func = ImportUtils::createModuleProperty;
550         return getElements(toscaJson, TypeUtils.ToscaTagNamesEnum.PROPERTIES, elementGenByName, func);
551     }
552
553     public static Either<Map<String, AttributeDefinition>, ResultStatusEnum> getAttributes(final Map<String, Object> toscaJson) {
554         final Function<String, AttributeDefinition> elementGenByName = ImportUtils::createAttribute;
555         final Function<Map<String, Object>, AttributeDefinition> func = ImportUtils::createModuleAttribute;
556         return getElements(toscaJson, ToscaTagNamesEnum.ATTRIBUTES, elementGenByName, func);
557     }
558
559     public static Either<Map<String, InputDefinition>, ResultStatusEnum> getInputs(Map<String, Object> toscaJson,
560                                                                                    AnnotationTypeOperations annotationTypeOperations) {
561         Function<String, InputDefinition> elementGenByName = ImportUtils::createInputs;
562         Function<Map<String, Object>, InputDefinition> func = object -> createModuleInput(object, annotationTypeOperations);
563         return getElements(toscaJson, TypeUtils.ToscaTagNamesEnum.INPUTS, elementGenByName, func);
564     }
565
566     public static Either<Map<String, OutputDefinition>, ResultStatusEnum> getOutputs(Map<String, Object> toscaJson) {
567         final Function<String, OutputDefinition> elementGenByName = ImportUtils::createOutputs;
568         final Function<Map<String, Object>, OutputDefinition> func = ImportUtils::createModuleOutput;
569         return getElements(toscaJson, TypeUtils.ToscaTagNamesEnum.OUTPUTS, elementGenByName, func);
570     }
571
572     public static Either<Map<String, InputDefinition>, ResultStatusEnum> getInputs(final Map<String, Object> toscaJson) {
573         return getElements(toscaJson, TypeUtils.ToscaTagNamesEnum.INPUTS, ImportUtils::createInputs, ImportUtils::createModuleInput);
574     }
575
576     private static <T> Either<Map<String, T>, ResultStatusEnum> getElements(Map<String, Object> toscaJson, TypeUtils.ToscaTagNamesEnum elementTagName,
577                                                                             Function<String, T> elementGenByName,
578                                                                             Function<Map<String, Object>, T> func) {
579         Either<Map<String, T>, ResultStatusEnum> eitherResult = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
580         Either<Map<String, Object>, ResultStatusEnum> toscaAttributes = findFirstToscaMapElement(toscaJson, elementTagName);
581         if (toscaAttributes.isLeft()) {
582             Map<String, Object> jsonAttributes = toscaAttributes.left().value();
583             Map<String, T> moduleAttributes = new HashMap<>();
584             Iterator<Entry<String, Object>> propertiesNameValue = jsonAttributes.entrySet().iterator();
585             while (propertiesNameValue.hasNext()) {
586                 Entry<String, Object> attributeNameValue = propertiesNameValue.next();
587                 if (attributeNameValue.getValue() instanceof Map) {
588                     @SuppressWarnings("unchecked") T attribute = func.apply((Map<String, Object>) attributeNameValue.getValue());
589                     if (attribute != null) {
590                         moduleAttributes.put(String.valueOf(attributeNameValue.getKey()), attribute);
591                     }
592                 } else {
593                     T element = elementGenByName.apply(String.valueOf(attributeNameValue.getValue()));
594                     moduleAttributes.put(String.valueOf(attributeNameValue.getKey()), element);
595                 }
596             }
597             if (moduleAttributes.size() > 0) {
598                 eitherResult = Either.left(moduleAttributes);
599             }
600         }
601         return eitherResult;
602     }
603
604     private static AttributeDefinition createAttribute(String name) {
605         AttributeDefinition attribute = new AttributeDefinition();
606         attribute.setName(name);
607         return attribute;
608     }
609
610     private static PropertyDefinition createProperties(String name) {
611         PropertyDefinition property = new PropertyDefinition();
612         property.setDefaultValue(name);
613         property.setName(name);
614         return property;
615     }
616
617     private static InputDefinition createInputs(String name) {
618         InputDefinition input = new InputDefinition();
619         input.setName(name);
620         return input;
621     }
622
623     private static OutputDefinition createOutputs(final String name) {
624         final OutputDefinition output = new OutputDefinition();
625         output.setName(name);
626         return output;
627     }
628
629     private static Annotation createAnnotation(String name) {
630         Annotation annotation = new Annotation();
631         annotation.setName(name);
632         return annotation;
633     }
634
635     static Either<List<HeatParameterDefinition>, ResultStatusEnum> getHeatParameters(Map<String, Object> heatData, String artifactType) {
636         Either<List<HeatParameterDefinition>, ResultStatusEnum> eitherResult = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
637         Either<Map<String, Object>, ResultStatusEnum> toscaProperties = findFirstToscaMapElement(heatData, TypeUtils.ToscaTagNamesEnum.PARAMETERS);
638         if (toscaProperties.isLeft()) {
639             Map<String, Object> jsonProperties = toscaProperties.left().value();
640             List<HeatParameterDefinition> moduleProperties = new ArrayList<>();
641             Iterator<Entry<String, Object>> propertiesNameValue = jsonProperties.entrySet().iterator();
642             while (propertiesNameValue.hasNext()) {
643                 Entry<String, Object> propertyNameValue = propertiesNameValue.next();
644                 if (propertyNameValue.getValue() instanceof Map || propertyNameValue.getValue() instanceof List) {
645                     if (!artifactType.equals(ArtifactTypeEnum.HEAT_ENV.getType())) {
646                         @SuppressWarnings("unchecked") Either<HeatParameterDefinition, ResultStatusEnum> propertyStatus = createModuleHeatParameter(
647                             (Map<String, Object>) propertyNameValue.getValue());
648                         if (propertyStatus.isRight()) {
649                             return Either.right(propertyStatus.right().value());
650                         }
651                         HeatParameterDefinition property = propertyStatus.left().value();
652                         property.setName(String.valueOf(propertyNameValue.getKey()));
653                         moduleProperties.add(property);
654                     } else {
655                         addHeatParamDefinition(moduleProperties, propertyNameValue, true);
656                     }
657                 } else {
658                     addHeatParamDefinition(moduleProperties, propertyNameValue, false);
659                 }
660             }
661             if (!isEmpty(moduleProperties)) {
662                 eitherResult = Either.left(moduleProperties);
663             }
664         }
665         return eitherResult;
666     }
667
668     private static void addHeatParamDefinition(List<HeatParameterDefinition> moduleProperties, Entry<String, Object> propertyNameValue,
669                                                boolean isJson) {
670         HeatParameterDefinition property = new HeatParameterDefinition();
671         Object value = propertyNameValue.getValue();
672         if (value != null) {
673             property.setDefaultValue(isJson ? new Gson().toJson(value) : StringEscapeUtils.escapeJava(String.valueOf(value)));
674         }
675         property.setName(String.valueOf(propertyNameValue.getKey()));
676         moduleProperties.add(property);
677     }
678
679     private static Either<HeatParameterDefinition, ResultStatusEnum> createModuleHeatParameter(Map<String, Object> propertyValue) {
680         HeatParameterDefinition propertyDef = new HeatParameterDefinition();
681         String type;
682         Either<String, ResultStatusEnum> propertyFieldType = findFirstToscaStringElement(propertyValue, TypeUtils.ToscaTagNamesEnum.TYPE);
683         if (propertyFieldType.isLeft()) {
684             type = propertyFieldType.left().value();
685             propertyDef.setType(type);
686         } else {
687             return Either.right(ResultStatusEnum.INVALID_PROPERTY_TYPE);
688         }
689         Either<String, ResultStatusEnum> propertyFieldDescription = findFirstToscaStringElement(propertyValue,
690             TypeUtils.ToscaTagNamesEnum.DESCRIPTION);
691         if (propertyFieldDescription.isLeft()) {
692             propertyDef.setDescription(propertyFieldDescription.left().value());
693         }
694         Either<Object, ResultStatusEnum> propertyFieldDefaultVal = findToscaElement(propertyValue, TypeUtils.ToscaTagNamesEnum.DEFAULT_VALUE,
695             ToscaElementTypeEnum.ALL);
696         if (propertyFieldDefaultVal.isLeft()) {
697             if (propertyFieldDefaultVal.left().value() == null) {
698                 return Either.right(ResultStatusEnum.INVALID_PROPERTY_VALUE);
699             }
700             Object value = propertyFieldDefaultVal.left().value();
701             String defaultValue =
702                 type.equals(HeatParameterType.JSON.getType()) ? new Gson().toJson(value) : StringEscapeUtils.escapeJava(String.valueOf(value));
703             propertyDef.setDefaultValue(defaultValue);
704             propertyDef.setCurrentValue(defaultValue);
705         }
706         return Either.left(propertyDef);
707     }
708
709     public static boolean containsGetInput(Object propValue) {
710         String value = getPropertyJsonStringValue(propValue, ToscaPropertyType.MAP.getType());
711         return value != null && value.contains(TypeUtils.ToscaTagNamesEnum.GET_INPUT.getElementName()) &&
712             !value.contains(TypeUtils.ToscaTagNamesEnum.CONCAT.getElementName()) && !value.contains("$");
713     }
714
715     public static String getPropertyJsonStringValue(Object value, String type) {
716         if (type == null) {
717             return null;
718         }
719         ToscaPropertyType validType = ToscaPropertyType.isValidType(type);
720         if (validType == null
721             || validType == ToscaPropertyType.JSON
722             || validType == ToscaPropertyType.MAP
723             || validType == ToscaPropertyType.LIST
724             || value instanceof Map) {
725             return (new Gson()).toJson(value);
726         }
727         return value.toString();
728     }
729
730     /**
731      * removes from Json map (toscaJson) first element found by name (elementName) note that this method could update the received argument toscaJson
732      *
733      * @param toscaJson
734      * @param elementName
735      */
736     public static void removeElementFromJsonMap(Map<String, Object> toscaJson, String elementName) {
737         for (Entry<String, Object> entry : toscaJson.entrySet()) {
738             String key = entry.getKey();
739             Object value = entry.getValue();
740             if (key.equals(elementName)) {
741                 toscaJson.remove(elementName);
742                 return;
743             } else if (value instanceof Map) {
744                 removeElementFromJsonMap((Map<String, Object>) value, elementName);
745             }
746         }
747     }
748
749     public enum ResultStatusEnum {
750         ELEMENT_NOT_FOUND, GENERAL_ERROR, OK, INVALID_PROPERTY_DEFAULT_VALUE, INVALID_PROPERTY_TYPE, INVALID_PROPERTY_VALUE,
751         MISSING_ENTRY_SCHEMA_TYPE, INVALID_PROPERTY_NAME, INVALID_ATTRIBUTE_NAME
752     }
753
754     public enum ToscaElementTypeEnum {
755         BOOLEAN, STRING, MAP, LIST, ALL
756     }
757
758     private static class CustomResolver extends Resolver {
759
760         @Override
761         protected void addImplicitResolvers() {
762             // avoid implicit resolvers for strings that can be interpreted as boolean values
763             addImplicitResolver(Tag.STR, EMPTY, "");
764             addImplicitResolver(Tag.STR, NULL, null);
765             addImplicitResolver(Tag.NULL, NULL, "~nN\0");
766             addImplicitResolver(Tag.NULL, EMPTY, null);
767             addImplicitResolver(Tag.INT, INT, "-+0123456789");
768             addImplicitResolver(Tag.FLOAT, FLOAT, "-+0123456789.");
769             addImplicitResolver(Tag.YAML, YAML, "!&*");
770         }
771     }
772
773     private static class YamlLoader extends YamlProcessor {
774
775         public Yaml getStrictYamlLoader() {
776             return createYaml();
777         }
778     }
779
780     @NoArgsConstructor(access = AccessLevel.PRIVATE)
781     public static class Constants {
782
783         public static final String FIRST_NON_CERTIFIED_VERSION = "0.1";
784         public static final String VENDOR_NAME = "ONAP (Tosca)";
785         public static final String VENDOR_RELEASE = "1.0.0.wd03";
786         public static final LifecycleStateEnum NORMATIVE_TYPE_LIFE_CYCLE = LifecycleStateEnum.CERTIFIED;
787         public static final LifecycleStateEnum NORMATIVE_TYPE_LIFE_CYCLE_NOT_CERTIFIED_CHECKOUT = LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT;
788         public static final boolean NORMATIVE_TYPE_HIGHEST_VERSION = true;
789         public static final String ABSTRACT_CATEGORY_NAME = "Generic";
790         public static final String ABSTRACT_SUBCATEGORY = "Abstract";
791         public static final String DEFAULT_ICON = "defaulticon";
792         public static final String INNER_VFC_DESCRIPTION = "Not reusable inner VFC";
793         public static final String USER_DEFINED_RESOURCE_NAMESPACE_PREFIX = "org.openecomp.resource.";
794         public static final String UI_JSON_PAYLOAD_NAME = "payloadName";
795         public static final String CVFC_DESCRIPTION = "Complex node type that is used as nested type in VF";
796         public static final String ESCAPED_DOUBLE_QUOTE = "\"";
797         public static final String QUOTE = "'";
798         public static final String VF_DESCRIPTION = "Nested VF in service";
799         public static final String TENANT = "tenant";
800
801     }
802 }