0b9432b3a150e31b15e8964696bb68369415df1c
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / csar / ToscaFunctionYamlParsingHandler.java
1 /*
2  * -
3  *  ============LICENSE_START=======================================================
4  *  Copyright (C) 2022 Nordix Foundation.
5  *  ================================================================================
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *       http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21
22 package org.openecomp.sdc.be.components.csar;
23
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.Optional;
28 import java.util.stream.Collectors;
29 import java.util.stream.Stream;
30 import org.apache.commons.lang3.StringUtils;
31 import org.openecomp.sdc.be.datatypes.elements.CustomYamlFunction;
32 import org.openecomp.sdc.be.datatypes.elements.ToscaConcatFunction;
33 import org.openecomp.sdc.be.datatypes.elements.ToscaCustomFunction;
34 import org.openecomp.sdc.be.datatypes.elements.ToscaFunction;
35 import org.openecomp.sdc.be.datatypes.elements.ToscaFunctionParameter;
36 import org.openecomp.sdc.be.datatypes.elements.ToscaFunctionType;
37 import org.openecomp.sdc.be.datatypes.elements.ToscaGetFunctionDataDefinition;
38 import org.openecomp.sdc.be.datatypes.elements.ToscaStringParameter;
39 import org.openecomp.sdc.be.datatypes.enums.PropertySource;
40 import org.openecomp.sdc.be.datatypes.tosca.ToscaGetFunctionType;
41
42 @org.springframework.stereotype.Component
43 public class ToscaFunctionYamlParsingHandler {
44
45     private static Optional<ToscaFunction> handleGetPropertyFunction(Map<String, Object> toscaFunctionPropertyValueMap, String functionType,
46                                                                      ToscaFunctionType toscaFunctionType) {
47         final ToscaGetFunctionDataDefinition toscaGetFunction = new ToscaGetFunctionDataDefinition();
48         toscaGetFunction.setFunctionType(
49             toscaFunctionType == ToscaFunctionType.GET_PROPERTY ? ToscaGetFunctionType.GET_PROPERTY : ToscaGetFunctionType.GET_ATTRIBUTE
50         );
51         final Object functionValueObj = toscaFunctionPropertyValueMap.get(functionType);
52         if (!(functionValueObj instanceof List)) {
53             return Optional.empty();
54         }
55         final List<String> functionParameters;
56         try {
57             functionParameters = ((List<Object>) functionValueObj).stream()
58                 .map(object -> Objects.toString(object, null))
59                 .collect(Collectors.toList());
60         } catch (final ClassCastException ignored) {
61             return Optional.empty();
62         }
63         if (functionParameters.size() < 2) {
64             return Optional.empty();
65         }
66         final String propertySourceType = functionParameters.get(0);
67         final PropertySource propertySource = PropertySource.findType(propertySourceType).orElse(null);
68         if (propertySource == PropertySource.SELF) {
69             toscaGetFunction.setPropertySource(propertySource);
70         } else {
71             toscaGetFunction.setPropertySource(PropertySource.INSTANCE);
72             toscaGetFunction.setSourceName(propertySourceType);
73         }
74         List<String> propertySourceIndex = functionParameters.subList(1, functionParameters.size());
75         String toscaIndexValue = propertySourceIndex.get((propertySourceIndex.size() - 1));
76         if (propertySourceIndex.size() > 1 && (toscaIndexValue.equalsIgnoreCase("INDEX") || StringUtils.isNumeric(toscaIndexValue))) {
77             toscaGetFunction.setPropertyPathFromSource(propertySourceIndex.subList(0,(propertySourceIndex.size() - 1)));
78             toscaGetFunction.setToscaIndex(toscaIndexValue);
79         } else {
80             toscaGetFunction.setPropertyPathFromSource(propertySourceIndex);
81         }
82         final String propertyName = toscaGetFunction.getPropertyPathFromSource().get(toscaGetFunction.getPropertyPathFromSource().size() - 1);
83         toscaGetFunction.setPropertyName(propertyName);
84         return Optional.of(toscaGetFunction);
85     }
86
87     private static Optional<ToscaFunction> handleGetInputFunction(Map<String, Object> toscaFunctionPropertyValueMap, String functionType) {
88         final ToscaGetFunctionDataDefinition toscaGetFunction = new ToscaGetFunctionDataDefinition();
89         toscaGetFunction.setFunctionType(ToscaGetFunctionType.GET_INPUT);
90         toscaGetFunction.setPropertySource(PropertySource.SELF);
91         final Object functionValueObj = toscaFunctionPropertyValueMap.get(functionType);
92         if (!(functionValueObj instanceof List) && !(functionValueObj instanceof String)) {
93             return Optional.empty();
94         }
95         if (functionValueObj instanceof String) {
96             toscaGetFunction.setPropertyPathFromSource(List.of((String) functionValueObj));
97         } else {
98             final List<String> functionParameters;
99             try {
100                 functionParameters = ((List<Object>) functionValueObj).stream()
101                     .map(object -> Objects.toString(object, null))
102                     .collect(Collectors.toList());
103             } catch (final ClassCastException ignored) {
104                 return Optional.empty();
105             }
106             String toscaIndexValue = functionParameters.get((functionParameters.size() - 1));
107             if (functionParameters.size() > 1 && (toscaIndexValue.equalsIgnoreCase("INDEX") || StringUtils.isNumeric(toscaIndexValue))) {
108                 toscaGetFunction.setPropertyPathFromSource(functionParameters.subList(0,(functionParameters.size() - 1)));
109                 toscaGetFunction.setToscaIndex(toscaIndexValue);
110             } else {
111                 toscaGetFunction.setPropertyPathFromSource(functionParameters);
112             }
113         }
114         final String propertyName = toscaGetFunction.getPropertyPathFromSource().get(toscaGetFunction.getPropertyPathFromSource().size() - 1);
115         toscaGetFunction.setPropertyName(propertyName);
116         return Optional.of(toscaGetFunction);
117     }
118
119     /**
120      * Builds a {@link ToscaFunction} based on the property value. It will build the object with the maximum information available in the property
121      * value, as not all the necessary information can be extracted from it. It will only parse values from supported functions in
122      * {@link ToscaFunctionType}.
123      *
124      * @param toscaFunctionPropertyValueMap the value of a property calls a TOSCA function
125      * @return the partially filled {@link ToscaFunction} object
126      */
127     public Optional<ToscaFunction> buildToscaFunctionBasedOnPropertyValue(final Map<String, Object> toscaFunctionPropertyValueMap) {
128         if (!isPropertyValueToscaFunction(toscaFunctionPropertyValueMap)) {
129             return Optional.empty();
130         }
131         final String functionType = toscaFunctionPropertyValueMap.keySet().iterator().next();
132         final ToscaFunctionType toscaFunctionType =
133             ToscaFunctionType.findType(functionType).orElse(functionType.startsWith("$") ? ToscaFunctionType.CUSTOM : null);
134         if (toscaFunctionType == null) {
135             return Optional.empty();
136         }
137         switch (toscaFunctionType) {
138             case GET_INPUT: {
139                 return handleGetInputFunction(toscaFunctionPropertyValueMap, functionType);
140             }
141             case GET_PROPERTY:
142             case GET_ATTRIBUTE: {
143                 return handleGetPropertyFunction(toscaFunctionPropertyValueMap, functionType, toscaFunctionType);
144             }
145             case CONCAT:
146                 return handleConcatFunction(toscaFunctionPropertyValueMap, functionType);
147             case CUSTOM:
148                 return handleCustomFunction(toscaFunctionPropertyValueMap, functionType);
149             default:
150                 return Optional.empty();
151         }
152     }
153
154     private Optional<ToscaFunction> handleCustomFunction(Map<String, Object> toscaFunctionPropertyValueMap, String functionType) {
155         final ToscaCustomFunction toscaCustomFunction = new ToscaCustomFunction();
156         toscaCustomFunction.setName(functionType.substring(1));
157         final Object functionValueObj = toscaFunctionPropertyValueMap.get(functionType);
158         if (!(functionValueObj instanceof List)) {
159             return Optional.empty();
160         }
161         final List<Object> functionParameters = (List<Object>) functionValueObj;
162         functionParameters.forEach(parameter -> {
163             if (parameter instanceof String) {
164                 final var stringParameter = new ToscaStringParameter();
165                 stringParameter.setValue((String) parameter);
166                 toscaCustomFunction.addParameter(stringParameter);
167                 return;
168             }
169             if (isPropertyValueToscaFunction(parameter)) {
170                 buildToscaFunctionBasedOnPropertyValue((Map<String, Object>) parameter).ifPresent(toscaFunction -> {
171                     if (toscaFunction instanceof ToscaFunctionParameter) {
172                         toscaCustomFunction.addParameter((ToscaFunctionParameter) toscaFunction);
173                     }
174                 });
175                 return;
176             }
177             final var customYamlFunction = new CustomYamlFunction();
178             customYamlFunction.setYamlValue(parameter);
179             toscaCustomFunction.addParameter(customYamlFunction);
180         });
181         return Optional.of(toscaCustomFunction);
182     }
183
184     /**
185      * Checks if the property value is a supported TOSCA function.
186      *
187      * @param propValueObj the value of a property
188      * @return {@code true} if the value is a supported TOSCA function, {@code false} otherwise
189      */
190     public boolean isPropertyValueToscaFunction(final Object propValueObj) {
191         if (propValueObj instanceof Map) {
192             final Map<String, Object> propValueMap = (Map<String, Object>) propValueObj;
193             if (propValueMap.keySet().size() > 1) {
194                 return false;
195             }
196             if (propValueMap.keySet().stream().anyMatch(keyValue -> keyValue.startsWith("$"))) {
197                 return true;
198             }
199
200             return Stream.of(ToscaFunctionType.GET_INPUT, ToscaFunctionType.GET_PROPERTY, ToscaFunctionType.GET_ATTRIBUTE, ToscaFunctionType.CONCAT)
201                 .anyMatch(type -> propValueMap.containsKey(type.getName()));
202         }
203         return false;
204     }
205
206     private Optional<ToscaFunction> handleConcatFunction(Map<String, Object> toscaFunctionPropertyValueMap, String functionType) {
207         final ToscaConcatFunction toscaConcatFunction = new ToscaConcatFunction();
208         final Object functionValueObj = toscaFunctionPropertyValueMap.get(functionType);
209         if (!(functionValueObj instanceof List)) {
210             return Optional.empty();
211         }
212         final List<Object> functionParameters = (List<Object>) functionValueObj;
213         if (functionParameters.size() < 2) {
214             return Optional.empty();
215         }
216         functionParameters.forEach(parameter -> {
217             if (parameter instanceof String) {
218                 final var stringParameter = new ToscaStringParameter();
219                 stringParameter.setValue((String) parameter);
220                 toscaConcatFunction.addParameter(stringParameter);
221                 return;
222             }
223             if (isPropertyValueToscaFunction(parameter)) {
224                 buildToscaFunctionBasedOnPropertyValue((Map<String, Object>) parameter).ifPresent(toscaFunction -> {
225                     if (toscaFunction instanceof ToscaFunctionParameter) {
226                         toscaConcatFunction.addParameter((ToscaFunctionParameter) toscaFunction);
227                     }
228                 });
229                 return;
230             }
231             final var customYamlFunction = new CustomYamlFunction();
232             customYamlFunction.setYamlValue(parameter);
233             toscaConcatFunction.addParameter(customYamlFunction);
234         });
235         return Optional.of(toscaConcatFunction);
236     }
237
238 }