2 * ============LICENSE_START=======================================================
4 * Copyright (C) 2020 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
10 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
20 package org.openecomp.sdc.be.model.operations.impl;
22 import com.google.gson.JsonElement;
23 import fj.data.Either;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Map.Entry;
29 import org.apache.commons.lang3.tuple.ImmutablePair;
30 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
31 import org.openecomp.sdc.be.dao.janusgraph.HealingJanusGraphGenericDao;
32 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
33 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
34 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
35 import org.openecomp.sdc.be.datatypes.elements.AttributeDataDefinition;
36 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
37 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
38 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
39 import org.openecomp.sdc.be.model.DataTypeDefinition;
40 import org.openecomp.sdc.be.model.PropertyDefinition;
41 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
42 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
43 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
44 import org.openecomp.sdc.be.resources.data.DataTypeData;
45 import org.openecomp.sdc.be.resources.data.PropertyData;
46 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
47 import org.openecomp.sdc.common.log.wrappers.Logger;
48 import org.springframework.beans.factory.annotation.Autowired;
49 import org.springframework.stereotype.Component;
51 @Component("attribute-operation")
52 public class AttributeOperation extends AbstractOperation {
54 private static final String FAILED_TO_FETCH_ATTRIBUTES_OF_DATA_TYPE = "Failed to fetch attributes of data type {}";
55 private static final String DATA_TYPE_CANNOT_BE_FOUND_IN_GRAPH_STATUS_IS = "Data type {} cannot be found in graph. status is {}";
56 private static final String THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID = "The value {} of attribute from type {} is invalid";
57 private static final Logger log = Logger.getLogger(AttributeOperation.class.getName());
60 public AttributeOperation(HealingJanusGraphGenericDao janusGraphGenericDao) {
61 this.janusGraphGenericDao = janusGraphGenericDao;
64 public boolean isAttributeTypeValid(final AttributeDataDefinition attributeDefinition) {
65 if (attributeDefinition == null) {
68 if (ToscaPropertyType.isValidType(attributeDefinition.getType()) == null) {
69 final Either<Boolean, JanusGraphOperationStatus> definedInDataTypes = isDefinedInDataTypes(attributeDefinition.getType());
70 if (definedInDataTypes.isRight()) {
73 Boolean isExist = definedInDataTypes.left().value();
74 return isExist.booleanValue();
80 private Either<Boolean, JanusGraphOperationStatus> isDefinedInDataTypes(final String propertyType) {
81 final String dataTypeUid = UniqueIdBuilder.buildDataTypeUid(null, propertyType);
82 final Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = getDataTypeByUid(dataTypeUid);
83 if (dataTypeByUid.isRight()) {
84 final JanusGraphOperationStatus status = dataTypeByUid.right().value();
85 if (status == JanusGraphOperationStatus.NOT_FOUND) {
86 return Either.left(false);
88 return Either.right(status);
90 return Either.left(true);
94 * Build Data type object from graph by unique id
96 private Either<DataTypeDefinition, JanusGraphOperationStatus> getDataTypeByUid(final String uniqueId) {
97 final Either<DataTypeData, JanusGraphOperationStatus> dataTypesRes = janusGraphGenericDao
98 .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, DataTypeData.class);
99 if (dataTypesRes.isRight()) {
100 JanusGraphOperationStatus status = dataTypesRes.right().value();
101 log.debug(DATA_TYPE_CANNOT_BE_FOUND_IN_GRAPH_STATUS_IS, uniqueId, status);
102 return Either.right(status);
104 final DataTypeData ctData = dataTypesRes.left().value();
105 final DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(ctData.getDataTypeDataDefinition());
106 final JanusGraphOperationStatus propertiesStatus = fillProperties(uniqueId, dataTypeDefinition);
107 if (propertiesStatus != JanusGraphOperationStatus.OK) {
108 log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, FAILED_TO_FETCH_ATTRIBUTES_OF_DATA_TYPE, uniqueId);
109 return Either.right(propertiesStatus);
111 final Either<ImmutablePair<DataTypeData, GraphEdge>, JanusGraphOperationStatus> parentNode = janusGraphGenericDao
112 .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.DataType, DataTypeData.class);
113 log.debug("After retrieving DERIVED_FROM node of {}. status is {}", uniqueId, parentNode);
114 if (parentNode.isRight()) {
115 final JanusGraphOperationStatus janusGraphOperationStatus = parentNode.right().value();
116 if (janusGraphOperationStatus != JanusGraphOperationStatus.NOT_FOUND) {
117 log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, "Failed to find the parent data type of data type {}. status is {}", uniqueId, janusGraphOperationStatus);
118 return Either.right(janusGraphOperationStatus);
121 // derived from node was found
122 final ImmutablePair<DataTypeData, GraphEdge> immutablePair = parentNode.left().value();
123 final DataTypeData parentCT = immutablePair.getKey();
124 final String parentUniqueId = parentCT.getUniqueId();
125 final Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = getDataTypeByUid(parentUniqueId);
126 if (dataTypeByUid.isRight()) {
127 return Either.right(dataTypeByUid.right().value());
129 final DataTypeDefinition parentDataTypeDefinition = dataTypeByUid.left().value();
130 dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
132 return Either.left(dataTypeDefinition);
135 private JanusGraphOperationStatus fillProperties(final String uniqueId, final DataTypeDefinition dataTypeDefinition) {
136 final Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode = this.findPropertiesOfNode(NodeTypeEnum.DataType, uniqueId);
137 if (findPropertiesOfNode.isRight()) {
138 final JanusGraphOperationStatus janusGraphOperationStatus = findPropertiesOfNode.right().value();
139 log.debug("After looking for properties of vertex {}. status is {}", uniqueId, janusGraphOperationStatus);
140 if (JanusGraphOperationStatus.NOT_FOUND.equals(janusGraphOperationStatus)) {
141 return JanusGraphOperationStatus.OK;
143 return janusGraphOperationStatus;
146 final Map<String, PropertyDefinition> properties = findPropertiesOfNode.left().value();
147 if (properties != null && !properties.isEmpty()) {
148 List<PropertyDefinition> listOfProps = new ArrayList<>();
149 for (final Entry<String, PropertyDefinition> entry : properties.entrySet()) {
150 final String propName = entry.getKey();
151 final PropertyDefinition propertyDefinition = entry.getValue();
152 final PropertyDefinition newPropertyDefinition = new PropertyDefinition(propertyDefinition);
153 newPropertyDefinition.setName(propName);
154 listOfProps.add(newPropertyDefinition);
156 dataTypeDefinition.setProperties(listOfProps);
158 return JanusGraphOperationStatus.OK;
162 private Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode(final NodeTypeEnum nodeType,
163 final String uniqueId) {
164 final Map<String, PropertyDefinition> resourceProps = new HashMap<>();
165 final Either<List<ImmutablePair<PropertyData, GraphEdge>>, JanusGraphOperationStatus> childrenNodes = janusGraphGenericDao
166 .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.PROPERTY, NodeTypeEnum.Property, PropertyData.class);
167 if (childrenNodes.isRight()) {
168 final JanusGraphOperationStatus operationStatus = childrenNodes.right().value();
169 return Either.right(operationStatus);
171 final List<ImmutablePair<PropertyData, GraphEdge>> values = childrenNodes.left().value();
172 if (values != null) {
173 for (final ImmutablePair<PropertyData, GraphEdge> immutablePair : values) {
174 final GraphEdge edge = immutablePair.getValue();
175 final String propertyName = (String) edge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
176 log.debug("Attribute {} is associated to node {}", propertyName, uniqueId);
177 final PropertyData propertyData = immutablePair.getKey();
178 final PropertyDefinition propertyDefinition = this.convertPropertyDataToPropertyDefinition(propertyData, propertyName);
179 resourceProps.put(propertyName, propertyDefinition);
182 log.debug("The properties associated to node {} are {}", uniqueId, resourceProps);
183 return Either.left(resourceProps);
186 private PropertyDefinition convertPropertyDataToPropertyDefinition(final PropertyData propertyDataResult, final String propertyName) {
187 log.debug("The object returned after create property is {}", propertyDataResult);
188 final PropertyDefinition propertyDefResult = new PropertyDefinition(propertyDataResult.getPropertyDataDefinition());
189 propertyDefResult.setConstraints(convertConstraints(propertyDataResult.getConstraints()));
190 propertyDefResult.setName(propertyName);
191 return propertyDefResult;
194 public ImmutablePair<String, Boolean> isAttributeInnerTypeValid(final AttributeDataDefinition attributeDefinition,
195 final Map<String, DataTypeDefinition> dataTypes) {
196 if (attributeDefinition == null) {
197 return new ImmutablePair<>(null, false);
199 SchemaDefinition schema;
200 PropertyDataDefinition innerProp;
201 String innerType = null;
202 if ((schema = attributeDefinition.getSchema()) != null && ((innerProp = schema.getProperty()) != null)) {
203 innerType = innerProp.getType();
205 final ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
206 if (innerToscaType == null) {
207 final DataTypeDefinition dataTypeDefinition = dataTypes.get(innerType);
208 if (dataTypeDefinition == null) {
209 log.debug("The inner type {} is not a data type.", innerType);
210 return new ImmutablePair<>(innerType, false);
212 log.debug("The inner type {} is a data type. Data type definition is {}", innerType, dataTypeDefinition);
215 return new ImmutablePair<>(innerType, true);
218 public boolean isAttributeDefaultValueValid(final AttributeDataDefinition attributeDefinition, final Map<String, DataTypeDefinition> dataTypes) {
219 if (attributeDefinition == null) {
223 String innerType = null;
224 final String propertyType = attributeDefinition.getType();
225 final ToscaPropertyType type = getType(propertyType);
226 if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
227 final SchemaDefinition def = attributeDefinition.getSchema();
231 final PropertyDataDefinition propDef = def.getProperty();
232 if (propDef == null) {
235 innerType = propDef.getType();
237 final String value = (String) attributeDefinition.get_default();
239 isValid = isValidValue(type, value, innerType, dataTypes);
241 log.trace("The given type {} is not a pre defined one.", propertyType);
242 final DataTypeDefinition foundDt = dataTypes.get(propertyType);
243 if (foundDt != null) {
244 isValid = isValidComplexValue(foundDt, value, dataTypes);
252 private boolean isValidComplexValue(final DataTypeDefinition foundDt, final String value, final Map<String, DataTypeDefinition> dataTypes) {
253 final ImmutablePair<JsonElement, Boolean> validateAndUpdate = dataTypeValidatorConverter.validateAndUpdate(value, foundDt, dataTypes);
254 log.trace("The result after validating complex value of type {} is {}", foundDt.getName(), validateAndUpdate);
255 return validateAndUpdate.right.booleanValue();
258 public StorageOperationStatus validateAndUpdateAttribute(final AttributeDataDefinition attributeDefinition,
259 final Map<String, DataTypeDefinition> dataTypes) {
260 log.trace("Going to validate attribute type and value. {}", attributeDefinition);
261 final String attributeDefinitionType = attributeDefinition.getType();
262 final String value = (String) attributeDefinition.get_default();
263 final ToscaPropertyType type = getType(attributeDefinitionType);
265 final DataTypeDefinition dataTypeDefinition = dataTypes.get(attributeDefinitionType);
266 if (dataTypeDefinition == null) {
267 log.debug("The type {} of attribute cannot be found.", attributeDefinitionType);
268 return StorageOperationStatus.INVALID_TYPE;
270 return validateAndUpdateAttributeComplexValue(attributeDefinition, attributeDefinitionType, value, dataTypeDefinition, dataTypes);
273 final Either<String, JanusGraphOperationStatus> checkInnerType = getInnerType(type, attributeDefinition::getSchema);
274 if (checkInnerType.isRight()) {
275 return StorageOperationStatus.INVALID_TYPE;
277 innerType = checkInnerType.left().value();
278 log.trace("After validating property type {}", attributeDefinitionType);
279 if (!isValidValue(type, value, innerType, dataTypes)) {
280 log.info(THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID, value, type);
281 return StorageOperationStatus.INVALID_VALUE;
283 final PropertyValueConverter converter = type.getConverter();
284 if (isEmptyValue(value)) {
285 log.debug("Default value was not sent for attribute {}. Set default value to {}", attributeDefinition.getName(), EMPTY_VALUE);
286 attributeDefinition.set_default(EMPTY_VALUE);
287 } else if (!isEmptyValue(value)) {
288 attributeDefinition.set_default(converter.convert(value, innerType, dataTypes));
290 return StorageOperationStatus.OK;
293 private StorageOperationStatus validateAndUpdateAttributeComplexValue(final AttributeDataDefinition attributeDefinition,
294 final String attributeType, final String value,
295 final DataTypeDefinition dataTypeDefinition,
296 final Map<String, DataTypeDefinition> dataTypes) {
297 final ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
298 if (!validateResult.right.booleanValue()) {
299 log.debug(THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID, attributeType, attributeType);
300 return StorageOperationStatus.INVALID_VALUE;
302 final JsonElement jsonElement = validateResult.left;
303 log.trace("Going to update value in attribute definition {} {}", attributeDefinition.getName(), (jsonElement != null ? jsonElement.toString() : null));
304 updateAttributeValue(attributeDefinition, jsonElement);
305 return StorageOperationStatus.OK;
308 private void updateAttributeValue(final AttributeDataDefinition attributeDefinition, final JsonElement jsonElement) {
309 attributeDefinition.set_default(jsonElement);
312 public Either<Object, Boolean> validateAndUpdateAttributeValue(final AttributeDataDefinition attribute,
313 final String innerType,
314 final Map<String, DataTypeDefinition> dataTypes) {
315 final var attributeType = attribute.getType();
316 final var value = attribute.getValue();
317 log.trace("Going to validate attribute value and its type. type = {}, value = {}", attributeType, value);
318 final var type = getType(attributeType);
320 final var dataTypeDefinition = dataTypes.get(attributeType);
321 final var validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
322 if (Boolean.FALSE.equals(validateResult.right)) {
323 log.debug(THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID, value, attributeType);
324 return Either.right(false);
326 return Either.left(getValueFromJsonElement(validateResult.left));
328 log.trace("before validating property type {}", attributeType);
329 if (!isValidValue(type, value, innerType, dataTypes)) {
330 log.debug(THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID, value, type);
331 return Either.right(false);
333 Object convertedValue = value;
334 if (!isEmptyValue(value)) {
335 convertedValue = type.getConverter().convert(value, innerType, dataTypes);
337 return Either.left(convertedValue);