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