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 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 public Either<Boolean, JanusGraphOperationStatus> isDefinedInDataTypes(final String propertyType) {
81 final String dataTypeUid = UniqueIdBuilder.buildDataTypeUid(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 public 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,
114 log.debug("After retrieving DERIVED_FROM node of {}. status is {}", uniqueId, parentNode);
115 if (parentNode.isRight()) {
116 final JanusGraphOperationStatus janusGraphOperationStatus = parentNode.right().value();
117 if (janusGraphOperationStatus != JanusGraphOperationStatus.NOT_FOUND) {
118 log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, "Failed to find the parent data type of data type {}. status is {}", uniqueId,
119 janusGraphOperationStatus);
120 return Either.right(janusGraphOperationStatus);
123 // derived from node was found
124 final ImmutablePair<DataTypeData, GraphEdge> immutablePair = parentNode.left().value();
125 final DataTypeData parentCT = immutablePair.getKey();
126 final String parentUniqueId = parentCT.getUniqueId();
127 final Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = getDataTypeByUid(parentUniqueId);
128 if (dataTypeByUid.isRight()) {
129 return Either.right(dataTypeByUid.right().value());
131 final DataTypeDefinition parentDataTypeDefinition = dataTypeByUid.left().value();
132 dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
134 return Either.left(dataTypeDefinition);
137 private JanusGraphOperationStatus fillProperties(final String uniqueId, final DataTypeDefinition dataTypeDefinition) {
138 final Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode = this
139 .findPropertiesOfNode(NodeTypeEnum.DataType, uniqueId);
140 if (findPropertiesOfNode.isRight()) {
141 final JanusGraphOperationStatus janusGraphOperationStatus = findPropertiesOfNode.right().value();
142 log.debug("After looking for properties of vertex {}. status is {}", uniqueId, janusGraphOperationStatus);
143 if (JanusGraphOperationStatus.NOT_FOUND.equals(janusGraphOperationStatus)) {
144 return JanusGraphOperationStatus.OK;
146 return janusGraphOperationStatus;
149 final Map<String, PropertyDefinition> properties = findPropertiesOfNode.left().value();
150 if (properties != null && !properties.isEmpty()) {
151 List<PropertyDefinition> listOfProps = new ArrayList<>();
152 for (final Entry<String, PropertyDefinition> entry : properties.entrySet()) {
153 final String propName = entry.getKey();
154 final PropertyDefinition propertyDefinition = entry.getValue();
155 final PropertyDefinition newPropertyDefinition = new PropertyDefinition(propertyDefinition);
156 newPropertyDefinition.setName(propName);
157 listOfProps.add(newPropertyDefinition);
159 dataTypeDefinition.setProperties(listOfProps);
161 return JanusGraphOperationStatus.OK;
165 public Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode(final NodeTypeEnum nodeType,
166 final String uniqueId) {
167 final Map<String, PropertyDefinition> resourceProps = new HashMap<>();
168 final Either<List<ImmutablePair<PropertyData, GraphEdge>>, JanusGraphOperationStatus> childrenNodes = janusGraphGenericDao
169 .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.PROPERTY, NodeTypeEnum.Property,
171 if (childrenNodes.isRight()) {
172 final JanusGraphOperationStatus operationStatus = childrenNodes.right().value();
173 return Either.right(operationStatus);
175 final List<ImmutablePair<PropertyData, GraphEdge>> values = childrenNodes.left().value();
176 if (values != null) {
177 for (final ImmutablePair<PropertyData, GraphEdge> immutablePair : values) {
178 final GraphEdge edge = immutablePair.getValue();
179 final String propertyName = (String) edge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
180 log.debug("Attribute {} is associated to node {}", propertyName, uniqueId);
181 final PropertyData propertyData = immutablePair.getKey();
182 final PropertyDefinition propertyDefinition = this.convertPropertyDataToPropertyDefinition(propertyData, propertyName);
183 resourceProps.put(propertyName, propertyDefinition);
186 log.debug("The properties associated to node {} are {}", uniqueId, resourceProps);
187 return Either.left(resourceProps);
190 public PropertyDefinition convertPropertyDataToPropertyDefinition(final PropertyData propertyDataResult, final String propertyName) {
191 log.debug("The object returned after create property is {}", propertyDataResult);
192 final PropertyDefinition propertyDefResult = new PropertyDefinition(propertyDataResult.getPropertyDataDefinition());
193 propertyDefResult.setConstraints(convertConstraints(propertyDataResult.getConstraints()));
194 propertyDefResult.setName(propertyName);
195 return propertyDefResult;
198 public ImmutablePair<String, Boolean> isAttributeInnerTypeValid(final AttributeDataDefinition attributeDefinition,
199 final Map<String, DataTypeDefinition> dataTypes) {
200 if (attributeDefinition == null) {
201 return new ImmutablePair<>(null, false);
203 SchemaDefinition schema;
204 PropertyDataDefinition innerProp;
205 String innerType = null;
206 if ((schema = attributeDefinition.getSchema()) != null && ((innerProp = schema.getProperty()) != null)) {
207 innerType = innerProp.getType();
209 final ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
210 if (innerToscaType == null) {
211 final DataTypeDefinition dataTypeDefinition = dataTypes.get(innerType);
212 if (dataTypeDefinition == null) {
213 log.debug("The inner type {} is not a data type.", innerType);
214 return new ImmutablePair<>(innerType, false);
216 log.debug("The inner type {} is a data type. Data type definition is {}", innerType, dataTypeDefinition);
219 return new ImmutablePair<>(innerType, true);
222 public boolean isAttributeDefaultValueValid(final AttributeDataDefinition attributeDefinition, final Map<String, DataTypeDefinition> dataTypes) {
223 if (attributeDefinition == null) {
227 String innerType = null;
228 final String propertyType = attributeDefinition.getType();
229 final ToscaPropertyType type = getType(propertyType);
230 if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
231 final SchemaDefinition def = attributeDefinition.getSchema();
235 final PropertyDataDefinition propDef = def.getProperty();
236 if (propDef == null) {
239 innerType = propDef.getType();
241 final String value = (String) attributeDefinition.get_default();
243 isValid = isValidValue(type, value, innerType, dataTypes);
245 log.trace("The given type {} is not a pre defined one.", propertyType);
246 final DataTypeDefinition foundDt = dataTypes.get(propertyType);
247 if (foundDt != null) {
248 isValid = isValidComplexValue(foundDt, value, dataTypes);
256 private boolean isValidComplexValue(final DataTypeDefinition foundDt, final String value, final Map<String, DataTypeDefinition> dataTypes) {
257 final ImmutablePair<JsonElement, Boolean> validateAndUpdate = dataTypeValidatorConverter.validateAndUpdate(value, foundDt, dataTypes);
258 log.trace("The result after validating complex value of type {} is {}", foundDt.getName(), validateAndUpdate);
259 return validateAndUpdate.right.booleanValue();
262 public StorageOperationStatus validateAndUpdateAttribute(final AttributeDataDefinition attributeDefinition,
263 final Map<String, DataTypeDefinition> dataTypes) {
264 log.trace("Going to validate attribute type and value. {}", attributeDefinition);
265 final String attributeDefinitionType = attributeDefinition.getType();
266 final String value = (String) attributeDefinition.get_default();
267 final ToscaPropertyType type = getType(attributeDefinitionType);
269 final DataTypeDefinition dataTypeDefinition = dataTypes.get(attributeDefinitionType);
270 if (dataTypeDefinition == null) {
271 log.debug("The type {} of attribute cannot be found.", attributeDefinitionType);
272 return StorageOperationStatus.INVALID_TYPE;
274 return validateAndUpdateAttributeComplexValue(attributeDefinition, attributeDefinitionType, value, dataTypeDefinition, dataTypes);
277 final Either<String, JanusGraphOperationStatus> checkInnerType = getInnerType(type, attributeDefinition::getSchema);
278 if (checkInnerType.isRight()) {
279 return StorageOperationStatus.INVALID_TYPE;
281 innerType = checkInnerType.left().value();
282 log.trace("After validating property type {}", attributeDefinitionType);
283 if (!isValidValue(type, value, innerType, dataTypes)) {
284 log.info(THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID, value, type);
285 return StorageOperationStatus.INVALID_VALUE;
287 final PropertyValueConverter converter = type.getConverter();
288 if (isEmptyValue(value)) {
289 log.debug("Default value was not sent for attribute {}. Set default value to {}", attributeDefinition.getName(), EMPTY_VALUE);
290 attributeDefinition.set_default(EMPTY_VALUE);
291 } else if (!isEmptyValue(value)) {
292 attributeDefinition.set_default(converter.convert(value, innerType, dataTypes));
294 return StorageOperationStatus.OK;
297 private StorageOperationStatus validateAndUpdateAttributeComplexValue(final AttributeDataDefinition attributeDefinition,
298 final String attributeType, final String value,
299 final DataTypeDefinition dataTypeDefinition,
300 final Map<String, DataTypeDefinition> dataTypes) {
301 final ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
302 if (!validateResult.right.booleanValue()) {
303 log.debug(THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID, attributeType, attributeType);
304 return StorageOperationStatus.INVALID_VALUE;
306 final JsonElement jsonElement = validateResult.left;
307 if (log.isTraceEnabled()) {
308 log.trace("Going to update value in attribute definition {} {}", attributeDefinition.getName(),
309 (jsonElement != null ? jsonElement.toString() : null));
311 updateAttributeValue(attributeDefinition, jsonElement);
312 return StorageOperationStatus.OK;
315 private void updateAttributeValue(final AttributeDataDefinition attributeDefinition, final JsonElement jsonElement) {
316 attributeDefinition.set_default(jsonElement);