cef4d8f6df5a2033298b1304448e01cdc7cf5ea3
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / AttributeBusinessLogic.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 java.util.List;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.Optional;
28 import org.apache.commons.lang3.tuple.ImmutablePair;
29 import org.openecomp.sdc.be.config.BeEcompErrorManager;
30 import org.openecomp.sdc.be.dao.api.ActionStatus;
31 import org.openecomp.sdc.be.datatypes.elements.AttributeDataDefinition;
32 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
33 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
34 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
35 import org.openecomp.sdc.be.model.DataTypeDefinition;
36 import org.openecomp.sdc.be.model.Resource;
37 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ArtifactsOperations;
38 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.InterfaceOperation;
39 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
40 import org.openecomp.sdc.be.model.operations.api.IGroupInstanceOperation;
41 import org.openecomp.sdc.be.model.operations.api.IGroupOperation;
42 import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
43 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
44 import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
45 import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
46 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
47 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
48 import org.openecomp.sdc.common.log.wrappers.Logger;
49 import org.openecomp.sdc.exception.ResponseFormat;
50 import org.springframework.beans.factory.annotation.Autowired;
51 import org.springframework.stereotype.Component;
52
53 /**
54  * This class holds the business logic relevant for attributes manipulation.
55  * 
56  * @author mshitrit
57  *
58  */
59 @Component("attributeBusinessLogic")
60 public class AttributeBusinessLogic extends BaseBusinessLogic {
61
62     private static final String CREATE_ATTRIBUTE = "CreateAttribute";
63     private static final String UPDATE_ATTRIBUTE = "UpdateAttribute";
64     private static final String DELETE_ATTRIBUTE = "DeleteAttribute";
65
66     private static final Logger log = Logger.getLogger(AttributeBusinessLogic.class);
67     private static final String FAILED_TO_LOCK_COMPONENT_ERROR = "Failed to lock component {}. Error - {}";
68
69     @Autowired
70     public AttributeBusinessLogic(IElementOperation elementDao,
71         IGroupOperation groupOperation,
72         IGroupInstanceOperation groupInstanceOperation,
73         IGroupTypeOperation groupTypeOperation,
74         InterfaceOperation interfaceOperation,
75         InterfaceLifecycleOperation interfaceLifecycleTypeOperation,
76         ArtifactsOperations artifactToscaOperation) {
77         super(elementDao, groupOperation, groupInstanceOperation, groupTypeOperation,
78             interfaceOperation, interfaceLifecycleTypeOperation, artifactToscaOperation);
79     }
80
81     /**
82      * Created attribute on the resource with resourceId
83      *
84      * @param resourceId
85      * @param newAttributeDef
86      * @param userId
87      * @return AttributeDefinition if created successfully Or ResponseFormat
88      */
89     public Either<AttributeDataDefinition, ResponseFormat> createAttribute(String resourceId, AttributeDataDefinition newAttributeDef, String userId) {
90         Either<AttributeDataDefinition, ResponseFormat> result = null;
91         validateUserExists(userId);
92
93         StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
94         if (lockResult != StorageOperationStatus.OK) {
95             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_ATTRIBUTE, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
96             log.info(FAILED_TO_LOCK_COMPONENT_ERROR, resourceId, lockResult);
97             return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
98         }
99
100         try {
101             // Get the resource from DB
102             Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
103             if (status.isRight()) {
104                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
105             }
106             Resource resource = status.left().value();
107
108             // verify that resource is checked-out and the user is the last updater
109             if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
110                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
111             }
112
113             // verify attribute does not exist in resource
114             if (isAttributeExist(resource.getAttributes(), resourceId, newAttributeDef.getName())) {
115                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.ATTRIBUTE_ALREADY_EXIST, newAttributeDef.getName()));
116             }
117             Map<String, DataTypeDefinition> eitherAllDataTypes = getAllDataTypes(applicationDataTypeCache);
118             // validate property default values
119             Either<Boolean, ResponseFormat> defaultValuesValidation = validateAttributeDefaultValue(newAttributeDef, eitherAllDataTypes);
120             if (defaultValuesValidation.isRight()) {
121                 return Either.right(defaultValuesValidation.right().value());
122             }
123
124             handleAttributeDefaultValue(newAttributeDef, eitherAllDataTypes);
125
126             // add the new attribute to resource on graph
127             // need to get StorageOperationStatus and convert to ActionStatus from
128             // componentsUtils
129             Either<AttributeDataDefinition, StorageOperationStatus> either = toscaOperationFacade.addAttributeOfResource(resource, newAttributeDef);
130             if (either.isRight()) {
131                 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
132                 return result;
133             }
134             result = Either.left(either.left().value());
135
136             return result;
137         } finally {
138             commitOrRollback(result);
139             graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
140         }
141     }
142
143     private Either<Boolean, ResponseFormat> validateAttributeDefaultValue(final AttributeDataDefinition attributeDefinition,
144                                                                           final Map<String, DataTypeDefinition> dataTypes) {
145
146
147         if (!attributeOperation.isAttributeTypeValid(attributeDefinition)) {
148             log.info("Invalid type for attribute '{}' type '{}'", attributeDefinition.getName(), attributeDefinition.getType());
149             final ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_PROPERTY_TYPE, attributeDefinition
150                 .getType(), attributeDefinition.getName());
151             return Either.right(responseFormat);
152         }
153         String type = attributeDefinition.getType();
154         String innerType = null;
155         if (type.equals(ToscaPropertyType.LIST.getType()) || type.equals(ToscaPropertyType.MAP.getType())) {
156             final ImmutablePair<String, Boolean> propertyInnerTypeValid = attributeOperation.isAttributeInnerTypeValid(
157                 attributeDefinition, dataTypes);
158             innerType = propertyInnerTypeValid.getLeft();
159             if (!propertyInnerTypeValid.getRight()) {
160                 log.info("Invalid inner type for attribute '{}' type '{}', dataTypeCount '{}'",
161                     attributeDefinition.getName(), attributeDefinition.getType(), dataTypes.size());
162                 final ResponseFormat responseFormat = componentsUtils
163                     .getResponseFormat(ActionStatus.INVALID_PROPERTY_INNER_TYPE, innerType, attributeDefinition.getName());
164                 return Either.right(responseFormat);
165             }
166         }
167         if (!attributeOperation.isAttributeDefaultValueValid(attributeDefinition, dataTypes)) {
168             log.info("Invalid default value for attribute '{}' type '{}'", attributeDefinition.getName(),
169                 attributeDefinition.getType());
170             ResponseFormat responseFormat;
171             if (type.equals(ToscaPropertyType.LIST.getType()) || type.equals(ToscaPropertyType.MAP.getType())) {
172                 responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_COMPLEX_DEFAULT_VALUE,
173                     attributeDefinition.getName(), type, innerType,
174                     (String) attributeDefinition.get_default());
175             } else {
176                 responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_DEFAULT_VALUE,
177                     attributeDefinition.getName(), type, (String) attributeDefinition.get_default());
178             }
179             return Either.right(responseFormat);
180
181         }
182         return Either.left(true);
183     }
184
185     private void handleAttributeDefaultValue(final AttributeDataDefinition newAttributeDefinition,
186                                              final Map<String, DataTypeDefinition> dataTypes) {
187         final ToscaPropertyType type = ToscaPropertyType.isValidType(newAttributeDefinition.getType());
188         final PropertyValueConverter converter = type.getConverter();
189         // get inner type
190         String innerType = null;
191         final SchemaDefinition schema = newAttributeDefinition.getSchema();
192         if (schema != null) {
193             final PropertyDataDefinition prop = schema.getProperty();
194             if (schema.getProperty() != null) {
195                 innerType = prop.getType();
196             }
197         }
198         if (newAttributeDefinition.get_default() != null) {
199             newAttributeDefinition.set_default(converter
200                 .convert((String) newAttributeDefinition.get_default(), innerType, dataTypes));
201         }
202     }
203
204     private boolean isAttributeExist(List<AttributeDataDefinition> attributes, String resourceUid, String propertyName) {
205         boolean isExist = false;
206         if (attributes != null) {
207             isExist = attributes.stream().anyMatch(p -> Objects.equals(p.getName(), propertyName) && Objects.equals(p.getOwnerId(), resourceUid));
208         }
209         return isExist;
210
211     }
212
213     /**
214      * @param resourceId
215      * @param attributeId
216      * @param userId
217      * @return
218      */
219     public Either<AttributeDataDefinition, ResponseFormat> getAttribute(String resourceId, String attributeId, String userId) {
220
221         validateUserExists(userId);
222
223         // Get the resource from DB
224         Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
225         if (status.isRight()) {
226             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
227         }
228         Resource resource = status.left().value();
229
230         List<AttributeDataDefinition> attributes = resource.getAttributes();
231         if (attributes == null) {
232             return Either.right(componentsUtils.getResponseFormat(ActionStatus.ATTRIBUTE_NOT_FOUND, ""));
233         } else {
234             // verify attribute exist in resource
235             Optional<AttributeDataDefinition> optionalAtt = attributes.stream().filter(att ->
236                 att.getUniqueId().equals(attributeId)).findAny();
237             return optionalAtt.<Either<AttributeDataDefinition, ResponseFormat>>map(Either::left).orElseGet(() ->
238                 Either.right(componentsUtils.getResponseFormat(ActionStatus.ATTRIBUTE_NOT_FOUND, "")));
239         }
240     }
241
242     /**
243      * Updates Attribute on resource
244      *
245      * @param resourceId
246      * @param attributeId
247      * @param newAttDef
248      * @param userId
249      * @return
250      */
251     public Either<AttributeDataDefinition, ResponseFormat> updateAttribute(String resourceId, String attributeId, AttributeDataDefinition newAttDef, String userId) {
252         Either<AttributeDataDefinition, ResponseFormat> result = null;
253
254         StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
255         if (lockResult != StorageOperationStatus.OK) {
256             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(UPDATE_ATTRIBUTE, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
257             log.info(FAILED_TO_LOCK_COMPONENT_ERROR, resourceId, lockResult);
258             return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
259         }
260         try {
261             // Get the resource from DB
262             Either<Resource, StorageOperationStatus> eitherResource = toscaOperationFacade.getToscaElement(resourceId);
263             if (eitherResource.isRight()) {
264                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
265             }
266             Resource resource = eitherResource.left().value();
267
268             // verify that resource is checked-out and the user is the last updater
269             if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
270                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
271             }
272
273             // verify attribute exist in resource
274             Either<AttributeDataDefinition, ResponseFormat> eitherAttribute = getAttribute(resourceId, attributeId, userId);
275             if (eitherAttribute.isRight()) {
276                 return Either.right(eitherAttribute.right().value());
277             }
278             Map<String, DataTypeDefinition> eitherAllDataTypes = getAllDataTypes(applicationDataTypeCache);
279
280             // validate attribute default values
281             Either<Boolean, ResponseFormat> defaultValuesValidation = validateAttributeDefaultValue(newAttDef, eitherAllDataTypes);
282             if (defaultValuesValidation.isRight()) {
283                 return Either.right(defaultValuesValidation.right().value());
284             }
285
286             // add the new property to resource on graph
287             StorageOperationStatus validateAndUpdateAttribute = attributeOperation.validateAndUpdateAttribute(newAttDef, eitherAllDataTypes);
288             if (validateAndUpdateAttribute != StorageOperationStatus.OK) {
289                 log.debug("Problem while updating attribute with id {}. Reason - {}", attributeId, validateAndUpdateAttribute);
290                 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(validateAndUpdateAttribute), resource.getName()));
291             }
292
293             Either<AttributeDataDefinition, StorageOperationStatus> eitherAttUpdate = toscaOperationFacade.updateAttributeOfResource(resource, newAttDef);
294
295             if (eitherAttUpdate.isRight()) {
296                 log.debug("Problem while updating attribute with id {}. Reason - {}", attributeId, eitherAttUpdate.right().value());
297                 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherAttUpdate.right().value()), resource.getName()));
298                 return result;
299             }
300
301             result = Either.left(eitherAttUpdate.left().value());
302             return result;
303         } finally {
304             commitOrRollback(result);
305             graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
306         }
307
308     }
309
310     /**
311      * Deletes Attribute on resource
312      *
313      * @param resourceId
314      * @param attributeId
315      * @param userId
316      * @return
317      */
318     public Either<AttributeDataDefinition, ResponseFormat> deleteAttribute(String resourceId, String attributeId, String userId) {
319
320         Either<AttributeDataDefinition, ResponseFormat> result = null;
321
322         validateUserExists(userId);
323
324         StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
325         if (lockResult != StorageOperationStatus.OK) {
326             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(DELETE_ATTRIBUTE, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
327             log.info(FAILED_TO_LOCK_COMPONENT_ERROR, resourceId, lockResult);
328             return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
329         }
330
331         try {
332             // Get the resource from DB
333             Either<Resource, StorageOperationStatus> eitherResource = toscaOperationFacade.getToscaElement(resourceId);
334             if (eitherResource.isRight()) {
335                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
336             }
337             Resource resource = eitherResource.left().value();
338
339             // verify that resource is checked-out and the user is the last updater
340             if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
341                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
342             }
343
344             // verify attribute exist in resource
345             Either<AttributeDataDefinition, ResponseFormat> eitherAttributeExist = getAttribute(resourceId, attributeId, userId);
346             if (eitherAttributeExist.isRight()) {
347                 return Either.right(eitherAttributeExist.right().value());
348             }
349             String attributeName = eitherAttributeExist.left().value().getName();
350
351             // delete attribute of resource from graph
352             StorageOperationStatus eitherAttributeDelete = toscaOperationFacade.deleteAttributeOfResource(resource, attributeName);
353             if (eitherAttributeDelete != StorageOperationStatus.OK) {
354                 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(eitherAttributeDelete), resource.getName()));
355                 return result;
356             }
357
358             result = Either.left(eitherAttributeExist.left().value());
359             return result;
360         } finally {
361             commitOrRollback(result);
362             graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
363         }
364     }
365 }