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