2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.openecomp.sdc.be.components.impl;
23 import fj.data.Either;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
30 import java.util.Optional;
32 import java.util.stream.Collectors;
33 import javax.annotation.Resource;
34 import org.apache.commons.lang3.StringUtils;
35 import org.apache.commons.lang3.tuple.ImmutablePair;
36 import org.openecomp.sdc.be.dao.api.ActionStatus;
37 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
38 import org.openecomp.sdc.be.impl.ComponentsUtils;
39 import org.openecomp.sdc.be.model.DataTypeDefinition;
40 import org.openecomp.sdc.be.model.Model;
41 import org.openecomp.sdc.be.model.PropertyDefinition;
42 import org.openecomp.sdc.be.model.normatives.ElementTypeEnum;
43 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
44 import org.openecomp.sdc.be.model.operations.impl.ModelOperation;
45 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
46 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
47 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
48 import org.openecomp.sdc.be.utils.TypeUtils;
49 import org.openecomp.sdc.common.log.wrappers.Logger;
50 import org.openecomp.sdc.exception.ResponseFormat;
51 import org.springframework.stereotype.Component;
53 @Component("dataTypeImportManager")
54 public class DataTypeImportManager {
56 private static final Logger log = Logger.getLogger(DataTypeImportManager.class.getName());
58 private PropertyOperation propertyOperation;
60 private ComponentsUtils componentsUtils;
62 private CommonImportManager commonImportManager;
64 private ModelOperation modelOperation;
66 public Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypes(final String dataTypeYml, final String modelName,
67 final boolean includeToModelDefaultImports) {
68 final var elementTypes = commonImportManager.createElementTypes(
69 dataTypeYml, dataTypesFromYml -> createDataTypesFromYml(dataTypeYml, modelName), this::createDataTypesByDao, ElementTypeEnum.DATA_TYPE);
71 if (includeToModelDefaultImports && StringUtils.isNotEmpty(modelName)) {
72 commonImportManager.addTypesToDefaultImports(ElementTypeEnum.DATA_TYPE, dataTypeYml, modelName);
77 private Either<List<DataTypeDefinition>, ActionStatus> createDataTypesFromYml(final String dataTypesYml, final String modelName) {
78 final Either<List<DataTypeDefinition>, ActionStatus> dataTypesEither = commonImportManager.createElementTypesFromYml(dataTypesYml,
79 this::createDataType);
80 if (dataTypesEither.isRight()) {
81 return dataTypesEither;
83 final List<DataTypeDefinition> dataTypes = dataTypesEither.left().value();
84 if (StringUtils.isNotEmpty(modelName)) {
85 final Optional<Model> modelOptional = modelOperation.findModelByName(modelName);
86 if (modelOptional.isPresent()) {
87 dataTypes.forEach(dataType -> dataType.setModel(modelName));
89 return Either.right(ActionStatus.INVALID_MODEL);
92 if (log.isTraceEnabled()) {
93 log.trace("Unsorted datatypes order:");
94 dataTypes.stream().forEach(dt -> log.trace(dt.getName()));
97 long startTime = System.currentTimeMillis();
98 List<DataTypeDefinition> sortedDataTypeDefinitions = sortDataTypesByDependencyOrder(dataTypes);
100 if (log.isTraceEnabled()) {
101 long sortTime = System.currentTimeMillis() - startTime;
102 log.trace("Sorting " + sortedDataTypeDefinitions.size() + " data types from model: " + modelName + " took: " + sortTime);
103 log.trace("Sorted datatypes order:");
104 sortedDataTypeDefinitions.stream().forEach(dt -> log.trace(dt.getName()));
106 return Either.left(sortedDataTypeDefinitions);
109 private List<DataTypeDefinition> sortDataTypesByDependencyOrder(final List<DataTypeDefinition> dataTypes) {
110 final List<DataTypeDefinition> sortedDataTypeDefinitions = new ArrayList<>();
111 final Map<String, DataTypeDefinition> dataTypeDefinitionsMap = new HashMap<>();
113 dataTypes.forEach(dataType -> {
115 int highestDependencyIndex = -1;
116 for (final String dependencyName : getDependencyTypes(dataType, dataTypes)) {
117 final DataTypeDefinition dependency = dataTypeDefinitionsMap.get(dependencyName);
118 final int indexOfDependency = sortedDataTypeDefinitions.lastIndexOf(dependency);
119 highestDependencyIndex = indexOfDependency > highestDependencyIndex ? indexOfDependency : highestDependencyIndex;
121 sortedDataTypeDefinitions.add(highestDependencyIndex + 1, dataType);
122 dataTypeDefinitionsMap.put(dataType.getName(), dataType);
126 return sortedDataTypeDefinitions;
129 private Collection<String> getDependencyTypes(final DataTypeDefinition dataType, final List<DataTypeDefinition> dataTypes) {
130 final Set<String> dependencies = new HashSet<>();
131 if (dataType.getDerivedFromName() != null) {
132 dependencies.add(dataType.getDerivedFromName());
134 if (dataType.getProperties() != null) {
135 dataType.getProperties().stream().forEach(property -> dependencies.add(property.getType()));
137 dataTypes.stream().filter(dependencyCandidate -> dependencies.contains(dependencyCandidate.getName()))
138 .forEach(dependencyDataType -> dependencies.addAll(getDependencyTypes(dependencyDataType, dataTypes)));
142 private Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypesByDao(
143 List<DataTypeDefinition> dataTypesToCreate) {
144 return commonImportManager.createElementTypesByDao(dataTypesToCreate, this::validateDataType,
145 dataType -> new ImmutablePair<>(ElementTypeEnum.DATA_TYPE, UniqueIdBuilder.buildDataTypeUid(dataType.getModel(), dataType.getName())),
146 dataTypeUid -> propertyOperation.getDataTypeByUidWithoutDerived(dataTypeUid, true),
147 dataType -> propertyOperation.addDataType(dataType),
148 (newDataType, oldDataType) -> propertyOperation.updateDataType(newDataType, oldDataType));
151 private Either<ActionStatus, ResponseFormat> validateDataType(DataTypeDefinition dataType) {
152 String dataTypeName = dataType.getName();
153 List<PropertyDefinition> properties = dataType.getProperties();
154 if (properties == null) {
155 // At least one parameter should be defined either in the properties
157 // section or at one of the parents
158 String derivedDataType = dataType.getDerivedFromName();
159 // If there are no properties, then we can create a data type if it
161 // is an abstract one or it derives from non abstract data type
162 if (derivedDataType == null || derivedDataType.isEmpty()) {
163 if (!isAbstract(dataType.getName()) && !ToscaPropertyType.isScalarType(dataTypeName)) {
164 log.debug("Data type {} must have properties unless it derives from non abstract data type", dataType.getName());
165 ResponseFormat responseFormat = componentsUtils
166 .getResponseFormatByDataType(ActionStatus.DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, dataType, null);
167 return Either.right(responseFormat);
170 if (!ToscaPropertyType.isScalarType(dataTypeName) && isAbstract(derivedDataType)) {
171 log.warn("Creating data type {} which derived from abstract data type with no properties", dataType.getName());
175 // properties tag cannot be empty
176 if (properties.isEmpty()) {
177 ResponseFormat responseFormat = componentsUtils
178 .getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY, dataType, null);
179 return Either.right(responseFormat);
181 // check no duplicates
182 Set<String> collect = properties.stream().map(PropertyDataDefinition::getName).collect(Collectors.toSet());
183 if (collect != null && properties.size() != collect.size()) {
184 ResponseFormat responseFormat = componentsUtils
185 .getResponseFormatByDataType(ActionStatus.DATA_TYPE_DUPLICATE_PROPERTY, dataType, null);
186 return Either.right(responseFormat);
188 List<String> propertiesWithSameTypeAsDataType = properties.stream().filter(p -> p.getType().equals(dataType.getName()))
189 .map(PropertyDataDefinition::getName).collect(Collectors.toList());
190 if (propertiesWithSameTypeAsDataType != null && !propertiesWithSameTypeAsDataType.isEmpty()) {
191 log.debug("The data type {} contains properties with the type {}", dataType.getName(), dataType.getName());
192 ResponseFormat responseFormat = componentsUtils
193 .getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE, dataType,
194 propertiesWithSameTypeAsDataType);
195 return Either.right(responseFormat);
198 String derivedDataType = dataType.getDerivedFromName();
199 if (derivedDataType != null) {
200 Either<DataTypeDefinition, StorageOperationStatus> derivedDataTypeByName = propertyOperation.getDataTypeByName(derivedDataType,
201 dataType.getModel());
202 if (derivedDataTypeByName.isRight()) {
203 StorageOperationStatus status = derivedDataTypeByName.right().value();
204 if (status == StorageOperationStatus.NOT_FOUND) {
205 ResponseFormat responseFormat = componentsUtils
206 .getResponseFormatByDataType(ActionStatus.DATA_TYPE_DERIVED_IS_MISSING, dataType, null);
207 return Either.right(responseFormat);
209 ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.GENERAL_ERROR, dataType, null);
210 return Either.right(responseFormat);
213 DataTypeDefinition derivedDataTypeDef = derivedDataTypeByName.left().value();
214 if (properties != null && !properties.isEmpty() && derivedDataTypeDef != null) {
215 if (isScalarType(derivedDataTypeDef)) {
216 ResponseFormat responseFormat = componentsUtils
217 .getResponseFormatByDataType(ActionStatus.DATA_TYPE_CANNOT_HAVE_PROPERTIES, dataType, null);
218 return Either.right(responseFormat);
220 Set<String> allParentsProps = new HashSet<>();
222 List<PropertyDefinition> currentParentsProps = derivedDataTypeDef.getProperties();
223 if (currentParentsProps != null) {
224 for (PropertyDefinition propertyDefinition : currentParentsProps) {
225 allParentsProps.add(propertyDefinition.getName());
228 derivedDataTypeDef = derivedDataTypeDef.getDerivedFrom();
229 } while (derivedDataTypeDef != null);
230 // Check that no property is already defined in one of the
233 Set<String> alreadyExistPropsCollection = properties.stream().filter(p -> allParentsProps.contains(p.getName()))
234 .map(PropertyDataDefinition::getName).collect(Collectors.toSet());
235 if (alreadyExistPropsCollection != null && !alreadyExistPropsCollection.isEmpty()) {
236 List<String> duplicateProps = new ArrayList<>();
237 duplicateProps.addAll(alreadyExistPropsCollection);
238 ResponseFormat responseFormat = componentsUtils
239 .getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR, dataType, duplicateProps);
240 return Either.right(responseFormat);
245 return Either.left(ActionStatus.OK);
248 private boolean isAbstract(String dataTypeName) {
249 ToscaPropertyType isPrimitiveToscaType = ToscaPropertyType.isValidType(dataTypeName);
250 return isPrimitiveToscaType != null && isPrimitiveToscaType.isAbstract();
253 private boolean isScalarType(DataTypeDefinition dataTypeDef) {
254 boolean isScalar = false;
255 DataTypeDefinition dataType = dataTypeDef;
256 while (dataType != null) {
257 String name = dataType.getName();
258 if (ToscaPropertyType.isScalarType(name)) {
262 dataType = dataType.getDerivedFrom();
267 private DataTypeDefinition createDataType(String dataTypeName, Map<String, Object> toscaJson) {
268 DataTypeDefinition dataType = new DataTypeDefinition();
269 dataType.setName(dataTypeName);
270 if (toscaJson != null) {
272 commonImportManager.setField(toscaJson, TypeUtils.ToscaTagNamesEnum.DESCRIPTION.getElementName(), dataType::setDescription);
274 commonImportManager.setField(toscaJson, TypeUtils.ToscaTagNamesEnum.DERIVED_FROM.getElementName(), dataType::setDerivedFromName);
276 CommonImportManager.setProperties(toscaJson, dataType::setProperties);