Sync Integ to Master
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / DataTypeImportManager.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 fj.data.Either;
24 import org.apache.commons.lang3.tuple.ImmutablePair;
25 import org.openecomp.sdc.be.components.impl.CommonImportManager.ElementTypeEnum;
26 import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaTagNamesEnum;
27 import org.openecomp.sdc.be.dao.api.ActionStatus;
28 import org.openecomp.sdc.be.impl.ComponentsUtils;
29 import org.openecomp.sdc.be.model.DataTypeDefinition;
30 import org.openecomp.sdc.be.model.PropertyDefinition;
31 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
32 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
33 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
34 import org.openecomp.sdc.exception.ResponseFormat;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37 import org.springframework.stereotype.Component;
38
39 import javax.annotation.Resource;
40 import java.util.ArrayList;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Set;
45 import java.util.function.Consumer;
46 import java.util.stream.Collectors;
47
48 @Component("dataTypeImportManager")
49 public class DataTypeImportManager {
50
51     public static void main(String[] args) {
52
53         List<PropertyDefinition> properties = new ArrayList<>();
54         PropertyDefinition propertyDefintion = new PropertyDefinition();
55         propertyDefintion.setName("aaa");
56         properties.add(propertyDefintion);
57
58         List<String> allParentsProps = new ArrayList<>();
59         allParentsProps.add("aaa");
60         allParentsProps.add("bbb");
61
62         Set<String> alreadyExistPropsCollection = properties.stream().filter(p -> allParentsProps.contains(p.getName())).map(p -> p.getName()).collect(Collectors.toSet());
63         System.out.println(alreadyExistPropsCollection);
64
65     }
66
67     private static final Logger log = LoggerFactory.getLogger(DataTypeImportManager.class);
68     @Resource
69     private PropertyOperation propertyOperation;
70     @Resource
71     private ComponentsUtils componentsUtils;
72     @Resource
73     private CommonImportManager commonImportManager;
74
75     public Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypes(String dataTypeYml) {
76         return commonImportManager.createElementTypes(dataTypeYml, elementTypeYml -> createDataTypesFromYml(elementTypeYml), elementTypesList -> createDataTypesByDao(elementTypesList), ElementTypeEnum.DataType);
77     }
78
79     private Either<List<DataTypeDefinition>, ActionStatus> createDataTypesFromYml(String dataTypesYml) {
80
81         return commonImportManager.createElementTypesFromYml(dataTypesYml, (dataTypeName, dataTypeJsonData) -> createDataType(dataTypeName, dataTypeJsonData));
82
83     }
84
85     private Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypesByDao(List<DataTypeDefinition> dataTypesToCreate) {
86
87         return commonImportManager.createElementTypesByDao(dataTypesToCreate, dataType -> validateDataType(dataType), dataType -> new ImmutablePair<>(ElementTypeEnum.DataType, dataType.getName()),
88                 dataTypeName -> propertyOperation.getDataTypeByNameWithoutDerived(dataTypeName), dataType -> propertyOperation.addDataType(dataType), (newDataType, oldDataType) -> propertyOperation.updateDataType(newDataType, oldDataType));
89     }
90
91     private Either<ActionStatus, ResponseFormat> validateDataType(DataTypeDefinition dataType) {
92
93         String dataTypeName = dataType.getName();
94         List<PropertyDefinition> properties = dataType.getProperties();
95         if (properties == null) {
96             // At least one parameter should be defined either in the properties
97             // section or at one of the parents
98             String derivedDataType = dataType.getDerivedFromName();
99             // If there are no properties, then we can create a data type if it
100             // is an abstract one or it derives from non abstract data type
101             if (derivedDataType == null || derivedDataType.isEmpty()) {
102                 if (!isAbstract(dataType.getName()) && !ToscaPropertyType.isScalarType(dataTypeName)) {
103                     log.debug("Data type {} must have properties unless it derives from non abstract data type", dataType.getName());
104                     ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, dataType, null);
105                     return Either.right(responseFormat);
106                 }
107             } else {
108                 // if it is not a scalar data type and it derives from abstract
109                 // data type, we should reject the request.
110                 if (!ToscaPropertyType.isScalarType(dataTypeName) && isAbstract(derivedDataType)) {
111                     log.debug("Data type {} which derived from abstract data type must have at least one property", dataType.getName());
112                     ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, dataType, null);
113                     return Either.right(responseFormat);
114                 }
115             }
116         } else {
117             // properties tag cannot be empty
118             if (properties.isEmpty()) {
119                 ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY, dataType, null);
120
121                 return Either.right(responseFormat);
122             }
123
124             // check no duplicates
125             Set<String> collect = properties.stream().map(p -> p.getName()).collect(Collectors.toSet());
126             if (collect != null) {
127                 if (properties.size() != collect.size()) {
128                     ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_DUPLICATE_PROPERTY, dataType, null);
129
130                     return Either.right(responseFormat);
131                 }
132             }
133
134             List<String> propertiesWithSameTypeAsDataType = properties.stream().filter(p -> p.getType().equals(dataType.getName())).map(p -> p.getName()).collect(Collectors.toList());
135             if (propertiesWithSameTypeAsDataType != null && propertiesWithSameTypeAsDataType.isEmpty() == false) {
136                 log.debug("The data type {} contains properties with the type {}", dataType.getName(), dataType.getName());
137                 ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE, dataType, propertiesWithSameTypeAsDataType);
138
139                 return Either.right(responseFormat);
140             }
141         }
142
143         String derivedDataType = dataType.getDerivedFromName();
144         if (derivedDataType != null) {
145             Either<DataTypeDefinition, StorageOperationStatus> derivedDataTypeByName = propertyOperation.getDataTypeByName(derivedDataType, true);
146             if (derivedDataTypeByName.isRight()) {
147                 StorageOperationStatus status = derivedDataTypeByName.right().value();
148                 if (status == StorageOperationStatus.NOT_FOUND) {
149                     ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_DERIVED_IS_MISSING, dataType, null);
150
151                     return Either.right(responseFormat);
152                 } else {
153                     ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.GENERAL_ERROR, dataType, null);
154
155                     return Either.right(responseFormat);
156
157                 }
158             } else {
159
160                 DataTypeDefinition derivedDataTypeDef = derivedDataTypeByName.left().value();
161                 if (properties != null && properties.isEmpty() == false) {
162
163                     if (true == isScalarType(derivedDataTypeDef)) {
164                         ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_CANNOT_HAVE_PROPERTIES, dataType, null);
165
166                         return Either.right(responseFormat);
167                     }
168
169                     Set<String> allParentsProps = new HashSet<>();
170                     do {
171                         List<PropertyDefinition> currentParentsProps = derivedDataTypeDef.getProperties();
172                         if (currentParentsProps != null) {
173                             for (PropertyDefinition propertyDefinition : currentParentsProps) {
174                                 allParentsProps.add(propertyDefinition.getName());
175                             }
176                         }
177                         derivedDataTypeDef = derivedDataTypeDef.getDerivedFrom();
178                     } while (derivedDataTypeDef != null);
179
180                     // Check that no property is already defined in one of the
181                     // ancestors
182                     Set<String> alreadyExistPropsCollection = properties.stream().filter(p -> allParentsProps.contains(p.getName())).map(p -> p.getName()).collect(Collectors.toSet());
183                     if (alreadyExistPropsCollection != null && alreadyExistPropsCollection.isEmpty() == false) {
184                         List<String> duplicateProps = new ArrayList<>();
185                         duplicateProps.addAll(alreadyExistPropsCollection);
186                         ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR, dataType, duplicateProps);
187
188                         return Either.right(responseFormat);
189                     }
190
191                 }
192             }
193         }
194         return Either.left(ActionStatus.OK);
195     }
196
197     private boolean isAbstract(String dataTypeName) {
198
199         ToscaPropertyType isPrimitiveToscaType = ToscaPropertyType.isValidType(dataTypeName);
200
201         return isPrimitiveToscaType != null && isPrimitiveToscaType.isAbstract() == true;
202
203     }
204
205     private boolean isScalarType(DataTypeDefinition dataTypeDef) {
206
207         boolean isScalar = false;
208         DataTypeDefinition dataType = dataTypeDef;
209
210         while (dataType != null) {
211
212             String name = dataType.getName();
213             if (ToscaPropertyType.isScalarType(name)) {
214                 isScalar = true;
215                 break;
216             }
217
218             dataType = dataType.getDerivedFrom();
219         }
220
221         return isScalar;
222     }
223
224     private DataTypeDefinition createDataType(String dataTypeName, Map<String, Object> toscaJson) {
225         DataTypeDefinition dataType = new DataTypeDefinition();
226
227         dataType.setName(dataTypeName);
228
229         if (toscaJson != null) {
230             // Description
231             final Consumer<String> descriptionSetter = description -> dataType.setDescription(description);
232             commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DESCRIPTION.getElementName(), descriptionSetter);
233             // Derived From
234             final Consumer<String> derivedFromSetter = derivedFrom -> dataType.setDerivedFromName(derivedFrom);
235             commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DERIVED_FROM.getElementName(), derivedFromSetter);
236             // Properties
237             commonImportManager.setProperties(toscaJson, (values) -> dataType.setProperties(values));
238
239             setConstraints(toscaJson, dataType);
240         }
241         return dataType;
242     }
243
244     private void setConstraints(Map<String, Object> toscaJson, DataTypeDefinition dataType) {
245         // TODO Auto-generated method stub
246
247     }
248
249 }