Support service import with custom tosca functions
[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.Optional;
27 import java.util.stream.Stream;
28 import org.openecomp.sdc.be.datatypes.elements.CustomYamlFunction;
29 import org.openecomp.sdc.be.datatypes.elements.ToscaConcatFunction;
30 import org.openecomp.sdc.be.datatypes.elements.ToscaCustomFunction;
31 import org.openecomp.sdc.be.datatypes.elements.ToscaFunction;
32 import org.openecomp.sdc.be.datatypes.elements.ToscaFunctionParameter;
33 import org.openecomp.sdc.be.datatypes.elements.ToscaFunctionType;
34 import org.openecomp.sdc.be.datatypes.elements.ToscaGetFunctionDataDefinition;
35 import org.openecomp.sdc.be.datatypes.elements.ToscaStringParameter;
36 import org.openecomp.sdc.be.datatypes.enums.PropertySource;
37 import org.openecomp.sdc.be.datatypes.tosca.ToscaGetFunctionType;
38
39 @org.springframework.stereotype.Component
40 public class ToscaFunctionYamlParsingHandler {
41
42     private static Optional<ToscaFunction> handleGetPropertyFunction(Map<String, Object> toscaFunctionPropertyValueMap, String functionType,
43                                                                      ToscaFunctionType toscaFunctionType) {
44         final ToscaGetFunctionDataDefinition toscaGetFunction = new ToscaGetFunctionDataDefinition();
45         toscaGetFunction.setFunctionType(
46             toscaFunctionType == ToscaFunctionType.GET_PROPERTY ? ToscaGetFunctionType.GET_PROPERTY : ToscaGetFunctionType.GET_ATTRIBUTE
47         );
48         final Object functionValueObj = toscaFunctionPropertyValueMap.get(functionType);
49         if (!(functionValueObj instanceof List)) {
50             return Optional.empty();
51         }
52         final List<String> functionParameters;
53         try {
54             functionParameters = (List<String>) functionValueObj;
55         } catch (final ClassCastException ignored) {
56             return Optional.empty();
57         }
58         if (functionParameters.size() < 2) {
59             return Optional.empty();
60         }
61         final String propertySourceType = functionParameters.get(0);
62         final PropertySource propertySource = PropertySource.findType(propertySourceType).orElse(null);
63         if (propertySource == PropertySource.SELF) {
64             toscaGetFunction.setPropertySource(propertySource);
65         } else {
66             toscaGetFunction.setPropertySource(PropertySource.INSTANCE);
67             toscaGetFunction.setSourceName(propertySourceType);
68         }
69         toscaGetFunction.setPropertyPathFromSource(functionParameters.subList(1, functionParameters.size()));
70         final String propertyName = toscaGetFunction.getPropertyPathFromSource().get(toscaGetFunction.getPropertyPathFromSource().size() - 1);
71         toscaGetFunction.setPropertyName(propertyName);
72         return Optional.of(toscaGetFunction);
73     }
74
75     private static Optional<ToscaFunction> handleGetInputFunction(Map<String, Object> toscaFunctionPropertyValueMap, String functionType) {
76         final ToscaGetFunctionDataDefinition toscaGetFunction = new ToscaGetFunctionDataDefinition();
77         toscaGetFunction.setFunctionType(ToscaGetFunctionType.GET_INPUT);
78         toscaGetFunction.setPropertySource(PropertySource.SELF);
79         final Object functionValueObj = toscaFunctionPropertyValueMap.get(functionType);
80         if (!(functionValueObj instanceof List) && !(functionValueObj instanceof String)) {
81             return Optional.empty();
82         }
83         if (functionValueObj instanceof String) {
84             toscaGetFunction.setPropertyPathFromSource(List.of((String) functionValueObj));
85         } else {
86             final List<String> functionParameters;
87             try {
88                 functionParameters = (List<String>) functionValueObj;
89             } catch (final ClassCastException ignored) {
90                 return Optional.empty();
91             }
92             toscaGetFunction.setPropertyPathFromSource(functionParameters);
93         }
94         final String propertyName = toscaGetFunction.getPropertyPathFromSource().get(toscaGetFunction.getPropertyPathFromSource().size() - 1);
95         toscaGetFunction.setPropertyName(propertyName);
96         return Optional.of(toscaGetFunction);
97     }
98
99     /**
100      * Builds a {@link ToscaFunction} based on the property value. It will build the object with the maximum information available in the property
101      * value, as not all the necessary information can be extracted from it. It will only parse values from supported functions in
102      * {@link ToscaFunctionType}.
103      *
104      * @param toscaFunctionPropertyValueMap the value of a property calls a TOSCA function
105      * @return the partially filled {@link ToscaFunction} object
106      */
107     public Optional<ToscaFunction> buildToscaFunctionBasedOnPropertyValue(final Map<String, Object> toscaFunctionPropertyValueMap) {
108         if (!isPropertyValueToscaFunction(toscaFunctionPropertyValueMap)) {
109             return Optional.empty();
110         }
111         final String functionType = toscaFunctionPropertyValueMap.keySet().iterator().next();
112         final ToscaFunctionType toscaFunctionType =
113             ToscaFunctionType.findType(functionType).orElse(functionType.startsWith("$") ? ToscaFunctionType.CUSTOM : null);
114         if (toscaFunctionType == null) {
115             return Optional.empty();
116         }
117         switch (toscaFunctionType) {
118             case GET_INPUT: {
119                 return handleGetInputFunction(toscaFunctionPropertyValueMap, functionType);
120             }
121             case GET_PROPERTY:
122             case GET_ATTRIBUTE: {
123                 return handleGetPropertyFunction(toscaFunctionPropertyValueMap, functionType, toscaFunctionType);
124             }
125             case CONCAT:
126                 return handleConcatFunction(toscaFunctionPropertyValueMap, functionType);
127             case CUSTOM:
128                 return handleCustomFunction(toscaFunctionPropertyValueMap, functionType);
129             default:
130                 return Optional.empty();
131         }
132     }
133
134     private Optional<ToscaFunction> handleCustomFunction(Map<String, Object> toscaFunctionPropertyValueMap, String functionType) {
135         final ToscaCustomFunction toscaCustomFunction = new ToscaCustomFunction();
136         toscaCustomFunction.setName(functionType.substring(1));
137         final Object functionValueObj = toscaFunctionPropertyValueMap.get(functionType);
138         if (!(functionValueObj instanceof List)) {
139             return Optional.empty();
140         }
141         final List<Object> functionParameters = (List<Object>) functionValueObj;
142         functionParameters.forEach(parameter -> {
143             if (parameter instanceof String) {
144                 final var stringParameter = new ToscaStringParameter();
145                 stringParameter.setValue((String) parameter);
146                 toscaCustomFunction.addParameter(stringParameter);
147                 return;
148             }
149             if (isPropertyValueToscaFunction(parameter)) {
150                 buildToscaFunctionBasedOnPropertyValue((Map<String, Object>) parameter).ifPresent(toscaFunction -> {
151                     if (toscaFunction instanceof ToscaFunctionParameter) {
152                         toscaCustomFunction.addParameter((ToscaFunctionParameter) toscaFunction);
153                     }
154                 });
155                 return;
156             }
157             final var customYamlFunction = new CustomYamlFunction();
158             customYamlFunction.setYamlValue(parameter);
159             toscaCustomFunction.addParameter(customYamlFunction);
160         });
161         return Optional.of(toscaCustomFunction);
162     }
163
164     /**
165      * Checks if the property value is a supported TOSCA function.
166      *
167      * @param propValueObj the value of a property
168      * @return {@code true} if the value is a supported TOSCA function, {@code false} otherwise
169      */
170     public boolean isPropertyValueToscaFunction(final Object propValueObj) {
171         if (propValueObj instanceof Map) {
172             final Map<String, Object> propValueMap = (Map<String, Object>) propValueObj;
173             if (propValueMap.keySet().size() > 1) {
174                 return false;
175             }
176             if (propValueMap.keySet().stream().anyMatch(keyValue -> keyValue.startsWith("$"))) {
177                 return true;
178             }
179
180             return Stream.of(ToscaFunctionType.GET_INPUT, ToscaFunctionType.GET_PROPERTY, ToscaFunctionType.GET_ATTRIBUTE, ToscaFunctionType.CONCAT)
181                 .anyMatch(type -> propValueMap.containsKey(type.getName()));
182         }
183         return false;
184     }
185
186     private Optional<ToscaFunction> handleConcatFunction(Map<String, Object> toscaFunctionPropertyValueMap, String functionType) {
187         final ToscaConcatFunction toscaConcatFunction = new ToscaConcatFunction();
188         final Object functionValueObj = toscaFunctionPropertyValueMap.get(functionType);
189         if (!(functionValueObj instanceof List)) {
190             return Optional.empty();
191         }
192         final List<Object> functionParameters = (List<Object>) functionValueObj;
193         if (functionParameters.size() < 2) {
194             return Optional.empty();
195         }
196         functionParameters.forEach(parameter -> {
197             if (parameter instanceof String) {
198                 final var stringParameter = new ToscaStringParameter();
199                 stringParameter.setValue((String) parameter);
200                 toscaConcatFunction.addParameter(stringParameter);
201                 return;
202             }
203             if (isPropertyValueToscaFunction(parameter)) {
204                 buildToscaFunctionBasedOnPropertyValue((Map<String, Object>) parameter).ifPresent(toscaFunction -> {
205                     if (toscaFunction instanceof ToscaFunctionParameter) {
206                         toscaConcatFunction.addParameter((ToscaFunctionParameter) toscaFunction);
207                     }
208                 });
209                 return;
210             }
211             final var customYamlFunction = new CustomYamlFunction();
212             customYamlFunction.setYamlValue(parameter);
213             toscaConcatFunction.addParameter(customYamlFunction);
214         });
215         return Optional.of(toscaConcatFunction);
216     }
217
218 }