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