re base code
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / AdditionalInformationBusinessLogic.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 org.openecomp.sdc.be.config.BeEcompErrorManager;
25 import org.openecomp.sdc.be.config.ConfigurationManager;
26 import org.openecomp.sdc.be.dao.api.ActionStatus;
27 import org.openecomp.sdc.be.dao.graph.datatype.AdditionalInformationEnum;
28 import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
29 import org.openecomp.sdc.be.datatypes.elements.AdditionalInfoParameterInfo;
30 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
31 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
32 import org.openecomp.sdc.be.model.AdditionalInformationDefinition;
33 import org.openecomp.sdc.be.model.operations.api.IAdditionalInformationOperation;
34 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
35 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
36 import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
37 import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
38 import org.openecomp.sdc.be.model.tosca.converters.StringConvertor;
39 import org.openecomp.sdc.be.model.tosca.validators.StringValidator;
40 import org.openecomp.sdc.common.api.Constants;
41 import org.openecomp.sdc.common.log.wrappers.Logger;
42 import org.openecomp.sdc.common.util.ValidationUtils;
43 import org.openecomp.sdc.exception.ResponseFormat;
44 import org.springframework.stereotype.Component;
45 import org.springframework.web.context.WebApplicationContext;
46
47 import javax.servlet.ServletContext;
48 import java.util.List;
49
50 @Component("additionalInformationBusinessLogic")
51 public class AdditionalInformationBusinessLogic extends BaseBusinessLogic {
52
53     private static final String CREATE_ADDITIONAL_INFORMATION = "CreateAdditionalInformation";
54
55     private static final String UPDATE_ADDITIONAL_INFORMATION = "UpdateAdditionalInformation";
56
57     private static final String DELETE_ADDITIONAL_INFORMATION = "DeleteAdditionalInformation";
58
59     private static final String GET_ADDITIONAL_INFORMATION = "GetAdditionalInformation";
60
61     private static final Logger log = Logger.getLogger(AdditionalInformationBusinessLogic.class.getName());
62     private static final String FAILED_TO_LOCK_COMPONENT_ERROR = "Failed to lock component {} error - {}";
63
64     @javax.annotation.Resource
65     private IAdditionalInformationOperation additionalInformationOperation = null;
66
67     protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
68         WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
69
70         WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
71
72         return webApplicationContext.getBean(class1);
73     }
74
75     /**
76      * Create new additional information on resource/service on graph
77      * @param nodeType
78      * @param resourceId
79      * @param additionalInfoParameterInfo
80      * @param userId
81      * @return Either<AdditionalInfoParameterInfo, ResponseFormat>
82      */
83     public Either<AdditionalInfoParameterInfo, ResponseFormat> createAdditionalInformation(NodeTypeEnum nodeType, String resourceId, AdditionalInfoParameterInfo additionalInfoParameterInfo, String userId) {
84
85         validateUserExists(userId, "create Additional Information", false);
86         Either<AdditionalInfoParameterInfo, ResponseFormat> result = null;
87
88         ResponseFormat responseFormat = verifyCanWorkOnComponent(nodeType, resourceId, userId);
89         if (responseFormat != null) {
90             result = Either.right(responseFormat);
91             return result;
92         }
93
94         // lock component
95         StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, nodeType);
96         if (!lockResult.equals(StorageOperationStatus.OK)) {
97             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_ADDITIONAL_INFORMATION, nodeType.getName(), resourceId);
98             log.info(FAILED_TO_LOCK_COMPONENT_ERROR, resourceId, lockResult);
99             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
100             return result;
101         }
102         try {
103             responseFormat = validateMaxSizeNotReached(nodeType, resourceId, additionalInfoParameterInfo);
104             if (responseFormat != null) {
105                 result = Either.right(responseFormat);
106                 return result;
107             }
108
109             // validate label
110             responseFormat = validateAndConvertKey(additionalInfoParameterInfo, CREATE_ADDITIONAL_INFORMATION);
111             if (responseFormat != null) {
112                 result = Either.right(responseFormat);
113                 return result;
114             }
115
116             // validate value
117             responseFormat = validateAndConvertValue(additionalInfoParameterInfo, CREATE_ADDITIONAL_INFORMATION);
118             if (responseFormat != null) {
119                 result = Either.right(responseFormat);
120                 return result;
121             }
122
123             Either<AdditionalInformationDefinition, StorageOperationStatus> addResult = additionalInformationOperation.createAdditionalInformationParameter(nodeType, resourceId, additionalInfoParameterInfo.getKey(),
124                     additionalInfoParameterInfo.getValue(), true);
125
126             if (addResult.isRight()) {
127                 StorageOperationStatus status = addResult.right().value();
128                 BeEcompErrorManager.getInstance().logBeSystemError(CREATE_ADDITIONAL_INFORMATION);
129                 ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
130                 result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.Label));
131                 return result;
132
133             } else {
134                 AdditionalInformationDefinition informationDefinition = addResult.left().value();
135
136                 AdditionalInfoParameterInfo createdAI = findAdditionInformationKey(informationDefinition.getParameters(), additionalInfoParameterInfo.getKey());
137                 result = Either.left(createdAI);
138                 return result;
139             }
140
141         } finally {
142             commitOrRollback(result);
143             // unlock component
144             graphLockOperation.unlockComponent(resourceId, nodeType);
145         }
146
147     }
148
149     /**
150      * Validate the value format. Format the value.
151      *
152      * @param additionalInfoParameterInfo
153      * @return null in case of success. Otherwise response format.
154      */
155     private ResponseFormat validateAndConvertValue(AdditionalInfoParameterInfo additionalInfoParameterInfo, String context) {
156         ResponseFormat result = null;
157
158         String value = additionalInfoParameterInfo.getValue();
159         log.debug("Going to validate additional information value {}", value);
160
161         Either<String, ResponseFormat> valueValidRes = validateValue(value);
162         if (valueValidRes.isRight()) {
163             BeEcompErrorManager.getInstance().logBeInvalidValueError(context, additionalInfoParameterInfo.getValue(), "additional information value", "string");
164             result = valueValidRes.right().value();
165         } else {
166             String newValue = valueValidRes.left().value();
167             if (log.isTraceEnabled()) {
168                 if (value != null && !value.equals(newValue)) {
169                     log.trace("The additional information value was normalized from {} to {}", value, newValue);
170                 }
171             }
172             additionalInfoParameterInfo.setValue(newValue);
173         }
174         return result;
175     }
176
177     /**
178      * @param additionalInfoParameterInfo
179      * @return
180      */
181     private ResponseFormat validateAndConvertKey(AdditionalInfoParameterInfo additionalInfoParameterInfo, String context) {
182
183         String key = additionalInfoParameterInfo.getKey();
184         log.debug("Going to validate additional information key {}", key);
185
186         ResponseFormat result = null;
187         ResponseFormat responseFormat;
188         Either<String, ResponseFormat> validateKeyRes = validateAndNormalizeKey(key);
189         if (validateKeyRes.isRight()) {
190             responseFormat = validateKeyRes.right().value();
191             BeEcompErrorManager.getInstance().logBeInvalidValueError(context, additionalInfoParameterInfo.getKey(), "additional information label", "string");
192             result = responseFormat;
193
194         } else {
195             String convertedKey = validateKeyRes.left().value();
196
197             if (log.isTraceEnabled() && key != null && !key.equals(convertedKey)) {
198                 log.trace("The additional information key {} was normalized to {}", key, convertedKey);
199             }
200             additionalInfoParameterInfo.setKey(convertedKey);
201         }
202         return result;
203     }
204
205     /**
206      * verify that the maximal number of additional information properties has not been reached.
207      *
208      * @param nodeType
209      * @param componentId
210      * @param additionalInfoParameterInfo
211      * @return response format in case the maximal number has been reached.
212      */
213     private ResponseFormat validateMaxSizeNotReached(NodeTypeEnum nodeType, String componentId, AdditionalInfoParameterInfo additionalInfoParameterInfo) {
214
215         ResponseFormat result;
216         Integer additionalInformationMaxNumberOfKeys = ConfigurationManager.getConfigurationManager().getConfiguration().getAdditionalInformationMaxNumberOfKeys();
217
218         Either<Integer, StorageOperationStatus> checkRes = additionalInformationOperation.getNumberOfAdditionalInformationParameters(nodeType, componentId, true);
219         if (checkRes.isRight()) {
220             StorageOperationStatus status = checkRes.right().value();
221
222             ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
223             result = componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None);
224             return result;
225         }
226         Integer currentNumberOfProperties = checkRes.left().value();
227         if (currentNumberOfProperties >= additionalInformationMaxNumberOfKeys) {
228             log.info("The current number of additional information properties is {}. The maximum allowed additional information properties is {}", currentNumberOfProperties, currentNumberOfProperties);
229             result = componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_MAX_NUMBER_REACHED, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None);
230             return result;
231         }
232
233         return null;
234     }
235
236     /**
237      * validate additional information value
238      *
239      * @param value
240      * @return
241      */
242     private Either<String, ResponseFormat> validateValue(String value) {
243
244         boolean isNonEmptyString = ValidationUtils.validateStringNotEmpty(value);
245         if (!isNonEmptyString) {
246             return Either.right(componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_EMPTY_STRING_NOT_ALLOWED));
247         }
248
249         boolean valid = StringValidator.getInstance().isValid(value, null);
250         if (!valid) {
251             return Either.right(componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_VALUE_NOT_ALLOWED_CHARACTERS, new AdditionalInfoParameterInfo(null, value), null, AdditionalInformationEnum.Value));
252         }
253
254         String converted = StringConvertor.getInstance().convert(value, null, null);
255
256         return Either.left(converted);
257     }
258
259     private AdditionalInfoParameterInfo findAdditionInformationKey(List<AdditionalInfoParameterInfo> parameters, String key) {
260
261         for (AdditionalInfoParameterInfo infoParameterInfo : parameters) {
262             if (infoParameterInfo.getKey().equals(key)) {
263                 return infoParameterInfo;
264             }
265         }
266         return null;
267     }
268
269     /**
270      * validate and normalize the key
271      * @param key
272      * @return Either<String, ResponseFormat>
273      */
274     private Either<String, ResponseFormat> validateAndNormalizeKey(String key) {
275
276         AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo();
277         additionalInfoParameterInfo.setKey(key);
278
279         String normKey = ValidationUtils.normalizeAdditionalInformation(key);
280         boolean isNonEmptyString = ValidationUtils.validateStringNotEmpty(normKey);
281         if (!isNonEmptyString) {
282             return Either.right(componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_EMPTY_STRING_NOT_ALLOWED, null, null, AdditionalInformationEnum.Label));
283         }
284         boolean isValidString = ValidationUtils.validateAdditionalInformationKeyName(normKey);
285         if (!isValidString) {
286             if (!ValidationUtils.validateLength(normKey, ValidationUtils.ADDITIONAL_INFORMATION_KEY_MAX_LENGTH)) {
287                 return Either.right(componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_EXCEEDS_LIMIT, additionalInfoParameterInfo, null, AdditionalInformationEnum.Label));
288             }
289             return Either.right(componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_KEY_NOT_ALLOWED_CHARACTERS, additionalInfoParameterInfo, null, AdditionalInformationEnum.Label));
290         }
291
292         return Either.left(normKey);
293     }
294
295     /**
296      * update key and value of a given additional information.
297      *
298      * @param nodeType
299      * @param resourceId
300      * @param additionalInfoParameterInfo
301      * @param userId
302      * @return
303      */
304     public Either<AdditionalInfoParameterInfo, ResponseFormat> updateAdditionalInformation(NodeTypeEnum nodeType, String resourceId, AdditionalInfoParameterInfo additionalInfoParameterInfo, String userId) {
305
306         validateUserExists(userId, "create Additional Information", false);
307         Either<AdditionalInfoParameterInfo, ResponseFormat> result = null;
308
309         ResponseFormat responseFormat = verifyCanWorkOnComponent(nodeType, resourceId, userId);
310         if (responseFormat != null) {
311             result = Either.right(responseFormat);
312             return result;
313         }
314         // lock component
315         StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, nodeType);
316         if (!lockResult.equals(StorageOperationStatus.OK)) {
317             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(UPDATE_ADDITIONAL_INFORMATION, nodeType.getName(), resourceId);
318             log.info(FAILED_TO_LOCK_COMPONENT_ERROR, resourceId, lockResult);
319             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
320             return result;
321         }
322         try {
323
324             // validate input
325             responseFormat = validateAndConvertKey(additionalInfoParameterInfo, UPDATE_ADDITIONAL_INFORMATION);
326             if (responseFormat != null) {
327                 result = Either.right(responseFormat);
328                 return result;
329             }
330
331             responseFormat = validateAndConvertValue(additionalInfoParameterInfo, UPDATE_ADDITIONAL_INFORMATION);
332             if (responseFormat != null) {
333                 result = Either.right(responseFormat);
334                 return result;
335             }
336
337             Either<AdditionalInformationDefinition, StorageOperationStatus> addResult = additionalInformationOperation.updateAdditionalInformationParameter(nodeType, resourceId, additionalInfoParameterInfo.getUniqueId(),
338                     additionalInfoParameterInfo.getKey(), additionalInfoParameterInfo.getValue(), true);
339
340             if (addResult.isRight()) {
341                 StorageOperationStatus status = addResult.right().value();
342                 BeEcompErrorManager.getInstance().logBeSystemError(UPDATE_ADDITIONAL_INFORMATION);
343                 ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
344                 result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None));
345                 return result;
346             } else {
347                 AdditionalInformationDefinition informationDefinition = addResult.left().value();
348                 AdditionalInfoParameterInfo parameterInfo = findAdditionInformationKey(informationDefinition.getParameters(), additionalInfoParameterInfo.getKey());
349                 result = Either.left(parameterInfo);
350                 return result;
351             }
352
353         } finally {
354             commitOrRollback(result);
355             // unlock component
356             graphLockOperation.unlockComponent(resourceId, nodeType);
357         }
358
359     }
360
361     /**
362      * Delete an additional information label
363      *
364      * @param nodeType
365      * @param resourceId
366      * @param additionalInfoParameterInfo
367      * @param userId
368      * @return
369      */
370     public Either<AdditionalInfoParameterInfo, ResponseFormat> deleteAdditionalInformation(NodeTypeEnum nodeType, String resourceId, AdditionalInfoParameterInfo additionalInfoParameterInfo, String userId) {
371
372         validateUserExists(userId, "delete Additional Information", false);
373         Either<AdditionalInfoParameterInfo, ResponseFormat> result = null;
374
375         ResponseFormat responseFormat = verifyCanWorkOnComponent(nodeType, resourceId, userId);
376         if (responseFormat != null) {
377             return Either.right(responseFormat);
378         }
379         // lock component
380         StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, nodeType);
381         if (!lockResult.equals(StorageOperationStatus.OK)) {
382             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(DELETE_ADDITIONAL_INFORMATION, nodeType.getName(), resourceId);
383             log.info(FAILED_TO_LOCK_COMPONENT_ERROR, resourceId, lockResult);
384             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
385             return result;
386         }
387
388         try {
389
390             Either<AdditionalInfoParameterInfo, StorageOperationStatus> findIdRes = additionalInformationOperation.getAdditionalInformationParameter(nodeType, resourceId, additionalInfoParameterInfo.getUniqueId(), true);
391             if (findIdRes.isRight()) {
392                 StorageOperationStatus status = findIdRes.right().value();
393                 if (status != StorageOperationStatus.NOT_FOUND) {
394                     BeEcompErrorManager.getInstance().logBeSystemError(GET_ADDITIONAL_INFORMATION);
395                 }
396                 ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
397                 result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None));
398                 return result;
399             }
400
401             AdditionalInfoParameterInfo foundAdditionalInfo = findIdRes.left().value();
402
403             Either<AdditionalInformationDefinition, StorageOperationStatus> addResult = additionalInformationOperation.deleteAdditionalInformationParameter(nodeType, resourceId, additionalInfoParameterInfo.getUniqueId(), true);
404
405             if (addResult.isRight()) {
406                 StorageOperationStatus status = addResult.right().value();
407                 BeEcompErrorManager.getInstance().logBeDaoSystemError(DELETE_ADDITIONAL_INFORMATION);
408                 ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
409                 result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None));
410                 return result;
411             } else {
412                 result = Either.left(foundAdditionalInfo);
413                 return result;
414             }
415
416         } finally {
417             commitOrRollback(result);
418             // unlock component
419             graphLockOperation.unlockComponent(resourceId, nodeType);
420         }
421
422     }
423
424     /**
425      * @param nodeType
426      * @param resourceId
427      * @param additionalInfoParameterInfo
428      * @param userId
429      * @return
430      */
431     public Either<AdditionalInfoParameterInfo, ResponseFormat> getAdditionalInformation(NodeTypeEnum nodeType, String resourceId, AdditionalInfoParameterInfo additionalInfoParameterInfo, String userId) {
432
433         validateUserExists(userId, "get Additional Information", false);
434         Either<AdditionalInfoParameterInfo, ResponseFormat> result = null;
435
436         try {
437
438             Either<AdditionalInfoParameterInfo, StorageOperationStatus> findIdRes = additionalInformationOperation.getAdditionalInformationParameter(nodeType, resourceId, additionalInfoParameterInfo.getUniqueId(), true);
439
440             if (findIdRes.isRight()) {
441                 StorageOperationStatus status = findIdRes.right().value();
442                 ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
443                 result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None));
444             }
445
446             AdditionalInfoParameterInfo foundAdditionalInfo = findIdRes.left().value();
447
448             result = Either.left(foundAdditionalInfo);
449
450             return result;
451
452         } finally {
453             commitOrRollback(result);
454         }
455
456     }
457
458     /**
459      * Get all additional information properties of a given resource/service
460      *
461      * @param nodeType
462      * @param resourceId
463      * @param userId
464      * @return
465      */
466     public Either<AdditionalInformationDefinition, ResponseFormat> getAllAdditionalInformation(NodeTypeEnum nodeType, String resourceId, String userId) {
467
468         validateUserExists(userId, "get All Additional Information", false);
469
470         Either<AdditionalInformationDefinition, ResponseFormat> result = null;
471
472         try {
473
474             Either<AdditionalInformationDefinition, TitanOperationStatus> findIdRes = additionalInformationOperation.getAllAdditionalInformationParameters(nodeType, resourceId, false);
475             if (findIdRes.isRight()) {
476                 StorageOperationStatus status = DaoStatusConverter.convertTitanStatusToStorageStatus(findIdRes.right().value());
477                 ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
478                 result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus));
479             } else {
480                 AdditionalInformationDefinition informationDefinition = findIdRes.left().value();
481                 result = Either.left(informationDefinition);
482             }
483
484             return result;
485
486         } finally {
487             commitOrRollback(result);
488         }
489
490     }
491
492     private ResponseFormat verifyCanWorkOnComponent(NodeTypeEnum nodeType, String resourceId, String userId) {
493
494         switch (nodeType) {
495         case Resource:
496
497             // verify that resource is checked-out and the user is the last
498             // updater
499             if (!ComponentValidationUtils.canWorkOnComponent(resourceId, toscaOperationFacade, userId)) {
500                 return componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
501             }
502             break;
503         case Service:
504
505             // verify that resource is checked-out and the user is the last
506             // updater
507             if (!ComponentValidationUtils.canWorkOnComponent(resourceId, toscaOperationFacade, userId)) {
508                 return componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
509             }
510             break;
511         default:
512             return componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT, nodeType.getName());
513         }
514
515         return null;
516     }
517
518 }