re base code
[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.dao.api.ActionStatus;
27 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
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.be.utils.TypeUtils;
35 import org.openecomp.sdc.common.log.wrappers.Logger;
36 import org.openecomp.sdc.exception.ResponseFormat;
37 import org.springframework.stereotype.Component;
38
39 import javax.annotation.Resource;
40 import java.util.*;
41 import java.util.stream.Collectors;
42
43 @Component("dataTypeImportManager")
44 public class DataTypeImportManager {
45
46     private static final Logger log = Logger.getLogger(DataTypeImportManager.class.getName());
47     @Resource
48     private PropertyOperation propertyOperation;
49     @Resource
50     private ComponentsUtils componentsUtils;
51     @Resource
52     private CommonImportManager commonImportManager;
53
54     public Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypes(String dataTypeYml) {
55         return commonImportManager.createElementTypes(dataTypeYml, this::createDataTypesFromYml, this::createDataTypesByDao, ElementTypeEnum.DATA_TYPE);
56     }
57
58     private Either<List<DataTypeDefinition>, ActionStatus> createDataTypesFromYml(String dataTypesYml) {
59         return commonImportManager.createElementTypesFromYml(dataTypesYml, this::createDataType);
60     }
61
62     private Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypesByDao(List<DataTypeDefinition> dataTypesToCreate) {
63
64         return commonImportManager.createElementTypesByDao(dataTypesToCreate, this::validateDataType, dataType -> new ImmutablePair<>(ElementTypeEnum.DATA_TYPE, dataType.getName()),
65                 dataTypeName -> propertyOperation.getDataTypeByNameWithoutDerived(dataTypeName), dataType -> propertyOperation.addDataType(dataType), (newDataType, oldDataType) -> propertyOperation.updateDataType(newDataType, oldDataType));
66     }
67
68     private Either<ActionStatus, ResponseFormat> validateDataType(DataTypeDefinition dataType) {
69
70         String dataTypeName = dataType.getName();
71         List<PropertyDefinition> properties = dataType.getProperties();
72         if (properties == null) {
73             // At least one parameter should be defined either in the properties
74             // section or at one of the parents
75             String derivedDataType = dataType.getDerivedFromName();
76             // If there are no properties, then we can create a data type if it
77             // is an abstract one or it derives from non abstract data type
78             if (derivedDataType == null || derivedDataType.isEmpty()) {
79                 if (!isAbstract(dataType.getName()) && !ToscaPropertyType.isScalarType(dataTypeName)) {
80                     log.debug("Data type {} must have properties unless it derives from non abstract data type", dataType.getName());
81                     ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, dataType, null);
82                     return Either.right(responseFormat);
83                 }
84             } else {
85                 // if it is not a scalar data type and it derives from abstract
86                 // data type, we should reject the request.
87                 if (!ToscaPropertyType.isScalarType(dataTypeName) && isAbstract(derivedDataType)) {
88                     log.debug("Data type {} which derived from abstract data type must have at least one property", dataType.getName());
89                     ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, dataType, null);
90                     return Either.right(responseFormat);
91                 }
92             }
93         } else {
94             // properties tag cannot be empty
95             if (properties.isEmpty()) {
96                 ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY, dataType, null);
97
98                 return Either.right(responseFormat);
99             }
100
101             // check no duplicates
102             Set<String> collect = properties.stream().map(PropertyDataDefinition::getName).collect(Collectors.toSet());
103             if (collect != null && properties.size() != collect.size()) {
104                 ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_DUPLICATE_PROPERTY, dataType, null);
105
106                 return Either.right(responseFormat);
107             }
108
109             List<String> propertiesWithSameTypeAsDataType = properties.stream().filter(p -> p.getType().equals(dataType.getName())).map(PropertyDataDefinition::getName).collect(Collectors.toList());
110             if (propertiesWithSameTypeAsDataType != null && !propertiesWithSameTypeAsDataType.isEmpty()) {
111                 log.debug("The data type {} contains properties with the type {}", dataType.getName(), dataType.getName());
112                 ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE, dataType, propertiesWithSameTypeAsDataType);
113
114                 return Either.right(responseFormat);
115             }
116         }
117
118         String derivedDataType = dataType.getDerivedFromName();
119         if (derivedDataType != null) {
120             Either<DataTypeDefinition, StorageOperationStatus> derivedDataTypeByName = propertyOperation.getDataTypeByName(derivedDataType, true);
121             if (derivedDataTypeByName.isRight()) {
122                 StorageOperationStatus status = derivedDataTypeByName.right().value();
123                 if (status == StorageOperationStatus.NOT_FOUND) {
124                     ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_DERIVED_IS_MISSING, dataType, null);
125
126                     return Either.right(responseFormat);
127                 } else {
128                     ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.GENERAL_ERROR, dataType, null);
129
130                     return Either.right(responseFormat);
131
132                 }
133             } else {
134
135                 DataTypeDefinition derivedDataTypeDef = derivedDataTypeByName.left().value();
136                 if (properties != null && !properties.isEmpty() && derivedDataTypeDef!=null) {
137
138                     if (isScalarType(derivedDataTypeDef)) {
139                         ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_CANNOT_HAVE_PROPERTIES, dataType, null);
140
141                         return Either.right(responseFormat);
142                     }
143
144                     Set<String> allParentsProps = new HashSet<>();
145                     do {
146                         List<PropertyDefinition> currentParentsProps = derivedDataTypeDef.getProperties();
147                         if (currentParentsProps != null) {
148                             for (PropertyDefinition propertyDefinition : currentParentsProps) {
149                                 allParentsProps.add(propertyDefinition.getName());
150                             }
151                         }
152                         derivedDataTypeDef = derivedDataTypeDef.getDerivedFrom();
153                     } while (derivedDataTypeDef != null);
154
155                     // Check that no property is already defined in one of the
156                     // ancestors
157                     Set<String> alreadyExistPropsCollection = properties.stream().filter(p -> allParentsProps.contains(p.getName())).map(PropertyDataDefinition::getName).collect(Collectors.toSet());
158                     if (alreadyExistPropsCollection != null && !alreadyExistPropsCollection.isEmpty()) {
159                         List<String> duplicateProps = new ArrayList<>();
160                         duplicateProps.addAll(alreadyExistPropsCollection);
161                         ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR, dataType, duplicateProps);
162
163                         return Either.right(responseFormat);
164                     }
165
166                 }
167             }
168         }
169         return Either.left(ActionStatus.OK);
170     }
171
172     private boolean isAbstract(String dataTypeName) {
173
174         ToscaPropertyType isPrimitiveToscaType = ToscaPropertyType.isValidType(dataTypeName);
175
176         return isPrimitiveToscaType != null && isPrimitiveToscaType.isAbstract();
177
178     }
179
180     private boolean isScalarType(DataTypeDefinition dataTypeDef) {
181
182         boolean isScalar = false;
183         DataTypeDefinition dataType = dataTypeDef;
184
185         while (dataType != null) {
186
187             String name = dataType.getName();
188             if (ToscaPropertyType.isScalarType(name)) {
189                 isScalar = true;
190                 break;
191             }
192
193             dataType = dataType.getDerivedFrom();
194         }
195
196         return isScalar;
197     }
198
199     private DataTypeDefinition createDataType(String dataTypeName, Map<String, Object> toscaJson) {
200         DataTypeDefinition dataType = new DataTypeDefinition();
201
202         dataType.setName(dataTypeName);
203
204         if (toscaJson != null) {
205             // Description
206             commonImportManager.setField(toscaJson, TypeUtils.ToscaTagNamesEnum.DESCRIPTION.getElementName(), dataType::setDescription);
207             // Derived From
208             commonImportManager.setField(toscaJson, TypeUtils.ToscaTagNamesEnum.DERIVED_FROM.getElementName(), dataType::setDerivedFromName);
209             // Properties
210             CommonImportManager.setProperties(toscaJson, dataType::setProperties);
211         }
212         return dataType;
213     }
214
215 }