Provide index token to tosca function for nested lists
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / validation / ToscaFunctionValidatorImpl.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.impl.validation;
23
24 import fj.data.Either;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.stream.Collectors;
28 import java.util.stream.Stream;
29 import org.apache.commons.collections.CollectionUtils;
30 import org.apache.commons.lang3.StringUtils;
31 import org.onap.sdc.tosca.datatypes.model.PropertyType;
32 import org.openecomp.sdc.be.components.impl.exceptions.ToscaFunctionExceptionSupplier;
33 import org.openecomp.sdc.be.components.impl.exceptions.ToscaGetFunctionExceptionSupplier;
34 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
35 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
36 import org.openecomp.sdc.be.datatypes.elements.ToscaGetFunctionDataDefinition;
37 import org.openecomp.sdc.be.datatypes.enums.PropertySource;
38 import org.openecomp.sdc.be.datatypes.tosca.ToscaGetFunctionType;
39 import org.openecomp.sdc.be.model.Component;
40 import org.openecomp.sdc.be.model.ComponentInstance;
41 import org.openecomp.sdc.be.model.DataTypeDefinition;
42 import org.openecomp.sdc.be.model.ToscaPropertyData;
43 import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
44 import org.openecomp.sdc.be.model.validation.ToscaFunctionValidator;
45
46 @org.springframework.stereotype.Component
47 public class ToscaFunctionValidatorImpl implements ToscaFunctionValidator {
48
49     private final ApplicationDataTypeCache applicationDataTypeCache;
50
51     public ToscaFunctionValidatorImpl(final ApplicationDataTypeCache applicationDataTypeCache) {
52         this.applicationDataTypeCache = applicationDataTypeCache;
53     }
54
55     @Override
56     public <T extends PropertyDataDefinition> void validate(T property, final Component containerComponent) {
57         if (property.getToscaFunction().getType() == null) {
58             throw ToscaFunctionExceptionSupplier.missingFunctionType().get();
59         }
60         if (property.isToscaGetFunction()) {
61             validateToscaGetFunction(property, containerComponent);
62         }
63     }
64
65     private <T extends PropertyDataDefinition> void validateToscaGetFunction(T property, Component parentComponent) {
66         final ToscaGetFunctionDataDefinition toscaGetFunction = (ToscaGetFunctionDataDefinition) property.getToscaFunction();
67         validateGetToscaFunctionAttributes(toscaGetFunction);
68         final ToscaGetFunctionType functionType = toscaGetFunction.getFunctionType();
69         validateGetPropertySource(functionType, toscaGetFunction.getPropertySource());
70         final String model = parentComponent.getModel();
71         switch (functionType) {
72             case GET_INPUT:
73                 validateGetFunction(property, parentComponent.getInputs(), model);
74                 break;
75             case GET_PROPERTY:
76                 if (toscaGetFunction.getPropertySource() == PropertySource.SELF) {
77                     validateGetFunction(property, parentComponent.getProperties(), model);
78                 } else if (toscaGetFunction.getPropertySource() == PropertySource.INSTANCE) {
79                     final ComponentInstance componentInstance =
80                         parentComponent.getComponentInstanceById(toscaGetFunction.getSourceUniqueId())
81                             .orElseThrow(ToscaGetFunctionExceptionSupplier.instanceNotFound(toscaGetFunction.getSourceName()));
82                     validateGetFunction(property, componentInstance.getProperties(), model);
83                 }
84                 break;
85             case GET_ATTRIBUTE:
86                 if (toscaGetFunction.getPropertySource() == PropertySource.SELF) {
87                     validateGetFunction(property, combine(parentComponent.getProperties(), parentComponent.getAttributes()), model);
88                 } else if (toscaGetFunction.getPropertySource() == PropertySource.INSTANCE) {
89                     final ComponentInstance componentInstance = parentComponent.getComponentInstanceById(toscaGetFunction.getSourceUniqueId())
90                         .orElseThrow(ToscaGetFunctionExceptionSupplier.instanceNotFound(toscaGetFunction.getSourceName()));
91                     validateGetFunction(property, combine(componentInstance.getProperties(), componentInstance.getAttributes()), model);
92                 }
93                 break;
94             default:
95                 throw ToscaGetFunctionExceptionSupplier.functionNotSupported(functionType).get();
96         }
97     }
98
99     private List<? extends ToscaPropertyData> combine(final List<? extends ToscaPropertyData> parentProperties,
100                                                      final List<? extends ToscaPropertyData> parentAttributes) {
101         if (CollectionUtils.isNotEmpty(parentProperties) && CollectionUtils.isNotEmpty(parentAttributes)) {
102             return Stream.concat(parentProperties.stream(), parentAttributes.stream()).collect(Collectors.toList());
103         }
104         if (CollectionUtils.isEmpty(parentProperties)) {
105             return parentAttributes;
106         }
107         return parentProperties;
108     }
109
110     private <T extends PropertyDataDefinition> void validateGetFunction(final T property,
111                                                                         final List<? extends ToscaPropertyData> parentProperties,
112                                                                         final String model) {
113         final ToscaGetFunctionDataDefinition toscaGetFunction = (ToscaGetFunctionDataDefinition) property.getToscaFunction();
114         if (CollectionUtils.isEmpty(parentProperties)) {
115             throw ToscaGetFunctionExceptionSupplier
116                 .propertyNotFoundOnTarget(toscaGetFunction.getPropertyName(), toscaGetFunction.getPropertySource(),
117                     toscaGetFunction.getFunctionType()
118                 ).get();
119         }
120         final String getFunctionPropertyUniqueId = toscaGetFunction.getPropertyUniqueId();
121         ToscaPropertyData referredProperty = parentProperties.stream()
122             .filter(property1 -> getFunctionPropertyUniqueId.equals(property1.getUniqueId()))
123             .findFirst()
124             .orElseThrow(ToscaGetFunctionExceptionSupplier
125                 .propertyNotFoundOnTarget(toscaGetFunction.getPropertyName(), toscaGetFunction.getPropertySource()
126                     , toscaGetFunction.getFunctionType())
127             );
128         if (toscaGetFunction.isSubProperty()) {
129             referredProperty = findSubProperty(referredProperty, toscaGetFunction, model);
130         }
131
132         if (!property.getType().equals(referredProperty.getType()) && !"list".equalsIgnoreCase(referredProperty.getType())) {
133             throw ToscaGetFunctionExceptionSupplier
134                 .propertyTypeDiverge(toscaGetFunction.getType(), referredProperty.getType(), property.getType()).get();
135         }
136         if (PropertyType.typeHasSchema(referredProperty.getType()) && !referredProperty.getSchemaType().equals(property.getType()) && !"list".equalsIgnoreCase(referredProperty.getType()) && !referredProperty.getSchemaType().equals(property.getSchemaType())) {
137             throw ToscaGetFunctionExceptionSupplier
138                 .propertySchemaDiverge(toscaGetFunction.getType(), referredProperty.getSchemaType(), property.getSchemaType()).get();
139         }
140     }
141
142     private void validateGetToscaFunctionAttributes(final ToscaGetFunctionDataDefinition toscaGetFunction) {
143         if (toscaGetFunction.getFunctionType() == null) {
144             throw ToscaGetFunctionExceptionSupplier.targetFunctionTypeNotFound().get();
145         }
146         if (toscaGetFunction.getPropertySource() == null) {
147             throw ToscaGetFunctionExceptionSupplier.targetPropertySourceNotFound(toscaGetFunction.getFunctionType()).get();
148         }
149         if (CollectionUtils.isEmpty(toscaGetFunction.getPropertyPathFromSource())) {
150             throw ToscaGetFunctionExceptionSupplier
151                 .targetSourcePathNotFound(toscaGetFunction.getFunctionType()).get();
152         }
153         if (StringUtils.isEmpty(toscaGetFunction.getSourceName()) || StringUtils.isBlank(toscaGetFunction.getSourceName())) {
154             throw ToscaGetFunctionExceptionSupplier.sourceNameNotFound(toscaGetFunction.getPropertySource()).get();
155         }
156         if (StringUtils.isEmpty(toscaGetFunction.getSourceUniqueId()) || StringUtils.isBlank(toscaGetFunction.getSourceUniqueId())) {
157             throw ToscaGetFunctionExceptionSupplier.sourceIdNotFound(toscaGetFunction.getPropertySource()).get();
158         }
159         if (StringUtils.isEmpty(toscaGetFunction.getPropertyName()) || StringUtils.isBlank(toscaGetFunction.getPropertyName())) {
160             throw ToscaGetFunctionExceptionSupplier.propertyNameNotFound(toscaGetFunction.getPropertySource()).get();
161         }
162         if (StringUtils.isEmpty(toscaGetFunction.getPropertyUniqueId()) || StringUtils.isBlank(toscaGetFunction.getPropertyUniqueId())) {
163             throw ToscaGetFunctionExceptionSupplier.propertyIdNotFound(toscaGetFunction.getPropertySource()).get();
164         }
165     }
166
167     private void validateGetPropertySource(final ToscaGetFunctionType functionType, final PropertySource propertySource) {
168         if (functionType == ToscaGetFunctionType.GET_INPUT && propertySource != PropertySource.SELF) {
169             throw ToscaGetFunctionExceptionSupplier
170                 .targetSourceNotSupported(functionType, propertySource).get();
171         }
172         if (functionType == ToscaGetFunctionType.GET_PROPERTY && !List.of(PropertySource.SELF, PropertySource.INSTANCE).contains(propertySource)) {
173             throw ToscaGetFunctionExceptionSupplier
174                 .targetSourceNotSupported(functionType, propertySource).get();
175         }
176     }
177
178     private ToscaPropertyData findSubProperty(final ToscaPropertyData referredProperty,
179                                               final ToscaGetFunctionDataDefinition toscaGetFunction,
180                                               final String model) {
181         final Map<String, DataTypeDefinition> dataTypeMap = loadDataTypes(model);
182         final List<String> propertyPathFromSource = toscaGetFunction.getPropertyPathFromSource();
183         DataTypeDefinition dataType = dataTypeMap.get(referredProperty.getType());
184         if (dataType == null) {
185             throw ToscaGetFunctionExceptionSupplier
186                 .propertyDataTypeNotFound(propertyPathFromSource.get(0), referredProperty.getType(), toscaGetFunction.getFunctionType()).get();
187         }
188         ToscaPropertyData foundProperty = referredProperty;
189         for (int i = 1; i < propertyPathFromSource.size(); i++) {
190             final String currentPropertyName = propertyPathFromSource.get(i);
191             foundProperty = dataType.getProperties().stream()
192                 .filter(propertyDefinition -> currentPropertyName.equals(propertyDefinition.getName())).findFirst()
193                 .orElseThrow(
194                     ToscaGetFunctionExceptionSupplier
195                         .propertyNotFoundOnTarget(propertyPathFromSource.subList(0, i), toscaGetFunction.getPropertySource(),
196                             toscaGetFunction.getFunctionType())
197                 );
198             dataType = dataTypeMap.get(foundProperty.getType());
199             if (dataType == null) {
200                 throw ToscaGetFunctionExceptionSupplier
201                     .propertyDataTypeNotFound(propertyPathFromSource.subList(0, i), foundProperty.getType(),
202                         toscaGetFunction.getFunctionType()).get();
203             }
204         }
205         return foundProperty;
206     }
207
208     private Map<String, DataTypeDefinition> loadDataTypes(String model) {
209         final Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> dataTypeEither =
210             applicationDataTypeCache.getAll(model);
211         if (dataTypeEither.isRight()) {
212             throw ToscaGetFunctionExceptionSupplier.couldNotLoadDataTypes(model).get();
213         }
214         return dataTypeEither.left().value();
215     }
216
217 }