Refactor catalog-be code
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / ProductBusinessLogic.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.components.impl.exceptions.ComponentException;
25 import org.openecomp.sdc.be.dao.api.ActionStatus;
26 import org.openecomp.sdc.be.datamodel.api.CategoryTypeEnum;
27 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
28 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
29 import org.openecomp.sdc.be.model.ComponentInstance;
30 import org.openecomp.sdc.be.model.LifecycleStateEnum;
31 import org.openecomp.sdc.be.model.Product;
32 import org.openecomp.sdc.be.model.User;
33 import org.openecomp.sdc.be.model.category.CategoryDefinition;
34 import org.openecomp.sdc.be.model.category.GroupingDefinition;
35 import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
36 import org.openecomp.sdc.be.model.operations.api.ICacheMangerOperation;
37 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
38 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
39 import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
40 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
41 import org.openecomp.sdc.be.ui.model.UiComponentDataTransfer;
42 import org.openecomp.sdc.be.user.Role;
43 import org.openecomp.sdc.common.log.wrappers.Logger;
44 import org.openecomp.sdc.common.util.ValidationUtils;
45 import org.openecomp.sdc.exception.ResponseFormat;
46 import org.springframework.beans.factory.annotation.Autowired;
47
48 import java.util.*;
49 import java.util.stream.Collectors;
50
51 @org.springframework.stereotype.Component("productBusinessLogic")
52 public class ProductBusinessLogic extends ComponentBusinessLogic {
53
54     private static final String PRODUCT_FULL_NAME = "full";
55     private static final String PRODUCT_ABBREVIATED_NAME = "abbreviated";
56     private static final Logger log = Logger.getLogger(ProductBusinessLogic.class);
57     private static final String INITIAL_VERSION = "0.1";
58     private static final String CREATE_PRODUCT = "Create Product";
59     private static List<Role> creationRoles;
60     private static List<Role> updateRoles;
61     private static List<Role> contactsRoles;
62
63     @Autowired
64     private ComponentInstanceBusinessLogic componentInstanceBusinessLogic;
65
66     @Autowired
67     private ICacheMangerOperation cacheManagerOperation;
68
69     public ProductBusinessLogic() {
70         creationRoles = new ArrayList<>();
71         updateRoles = new ArrayList<>();
72         contactsRoles = new ArrayList<>();
73
74         // only PM is allowed to create/update products
75         creationRoles.add(Role.PRODUCT_MANAGER);
76         updateRoles.add(Role.PRODUCT_MANAGER);
77         // Only PM is allowed to be product contacts
78         contactsRoles.add(Role.PRODUCT_MANAGER);
79     }
80
81
82     public Either<Product, ResponseFormat> createProduct(Product product, User user) {
83         AuditingActionEnum actionEnum = AuditingActionEnum.CREATE_RESOURCE;
84         ComponentTypeEnum typeEnum = ComponentTypeEnum.PRODUCT;
85
86         // validate user - should be first to get the maximum auditing info in
87         // case of subsequent failures
88         log.debug("get user from DB");
89         user = validateUser(user, CREATE_PRODUCT, product, actionEnum, false);
90         // validate user role
91         validateUserRole(user, product, creationRoles, actionEnum, null);
92
93         if (product == null) {
94             log.debug("Invalid product json. Check product servlet log for createProduct entry params");
95             ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
96             componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, typeEnum);
97             return Either.right(responseFormat);
98         }
99
100         // warn about non-updatable fields
101         checkUnupdatableProductFields(product);
102
103         Either<Product, ResponseFormat> validateProductResponse = validateProductBeforeCreate(product, user, actionEnum);
104         if (validateProductResponse.isRight()) {
105             return Either.right(validateProductResponse.right().value());
106         }
107
108         log.debug("send product {} to dao for create", product.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
109
110         Either<Boolean, ResponseFormat> lockResult = lockComponentByName(product.getSystemName(), product, CREATE_PRODUCT);
111         if (lockResult.isRight()) {
112             ResponseFormat responseFormat = lockResult.right().value();
113             componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
114             return Either.right(responseFormat);
115         }
116
117         log.debug("Product name locked is {}, status = {}", product.getSystemName(), lockResult);
118
119         try {
120             Either<Product, StorageOperationStatus> createProductEither = toscaOperationFacade.createToscaComponent(product);
121
122             if (createProductEither.isRight()) {
123                 ResponseFormat responseFormat = componentsUtils.getResponseFormatByComponent(componentsUtils.convertFromStorageResponse(createProductEither.right().value()), product, typeEnum);
124                 componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, typeEnum);
125                 return Either.right(responseFormat);
126             }
127
128             log.debug("Product created successfully");
129             ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.CREATED);
130             componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, typeEnum);
131
132             Product createdProduct = createProductEither.left().value();
133
134             return Either.left(createdProduct);
135
136         } finally {
137             graphLockOperation.unlockComponentByName(product.getSystemName(), product.getUniqueId(), NodeTypeEnum.Product);
138         }
139
140     }
141
142     private void checkUnupdatableProductFields(Product product) {
143         checkComponentFieldsForOverrideAttempt(product);
144         if (product.getNormalizedName() != null) {
145             log.info("NormalizedName cannot be defined by user. This field will be overridden by the application");
146         }
147     }
148
149     private Either<Product, ResponseFormat> validateProductBeforeCreate(Product product, User user, AuditingActionEnum actionEnum) {
150
151         Either<Boolean, ResponseFormat> validateProductFields = validateProductFieldsBeforeCreate(user, product, actionEnum);
152         if (validateProductFields.isRight()) {
153             return Either.right(validateProductFields.right().value());
154         }
155
156         if (product.getIsActive() == null) {
157             log.debug("no isActive value was provided, setting to default: false");
158             product.setIsActive(false);
159         }
160
161         product.setCreatorUserId(user.getUserId());
162
163         // enrich object
164         log.debug("enrich product with version and state");
165         product.setState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
166         product.setVersion(INITIAL_VERSION);
167
168         // Generate invariant UUID - must be here and not in operation since it
169         // should stay constant during clone
170         String invariantUUID = UniqueIdBuilder.buildInvariantUUID();
171         product.setInvariantUUID(invariantUUID);
172
173         return Either.left(product);
174     }
175
176     private Either<Boolean, ResponseFormat> validateProductFieldsBeforeCreate(User user, Product product, AuditingActionEnum actionEnum) {
177
178         // To be removed in 1607
179         // See below
180         String oldName = product.getName();
181
182         Either<Boolean, ResponseFormat> componentNameValidation = validateProductNameAndCleanup(user, product, actionEnum);
183         if (componentNameValidation.isRight()) {
184             return componentNameValidation;
185         }
186
187         Either<Boolean, ResponseFormat> componentNameUniquenessValidation = validateComponentNameUnique(user, product, actionEnum);
188         if (componentNameUniquenessValidation.isRight()) {
189             return componentNameUniquenessValidation;
190         }
191
192         // To be removed in 1607 and replaced with generic
193         // validateTagsListAndRemoveDuplicates()
194         // See comments on the validateTagsListAndRemoveDuplicates(user,
195         // product, oldName, actionEnum) function
196         Either<Boolean, ResponseFormat> tagsValidation = validateTagsListAndRemoveDuplicates(user, product, oldName, actionEnum);
197         if (tagsValidation.isRight()) {
198             return tagsValidation;
199         }
200
201         validateIcon(user, product, actionEnum);
202
203         Either<Boolean, ResponseFormat> projectCodeValidation = validateProjectCode(user, product, actionEnum);
204         if (projectCodeValidation.isRight()) {
205             return projectCodeValidation;
206         }
207         Either<Boolean, ResponseFormat> categoryValidation = validateGrouping(user, product, actionEnum);
208         if (categoryValidation.isRight()) {
209             return categoryValidation;
210         }
211
212         Either<Boolean, ResponseFormat> contactsListValidation = validateAndUpdateProductContactsList(user, product, actionEnum);
213         if (contactsListValidation.isRight()) {
214             return contactsListValidation;
215         }
216
217         Either<Boolean, ResponseFormat> productFullNameValidation = validateProductFullNameAndCleanup(user, product, actionEnum);
218         if (productFullNameValidation.isRight()) {
219             return productFullNameValidation;
220         }
221
222         validateDescriptionAndCleanup(user, product, actionEnum);
223
224         return Either.left(true);
225     }
226
227     public Either<Map<String, Boolean>, ResponseFormat> validateProductNameExists(String productName, String userId) {
228
229         validateUserExists(userId, "validate Product Name Exists", false);
230         Either<Boolean, StorageOperationStatus> dataModelResponse = toscaOperationFacade.validateComponentNameUniqueness(productName, null, ComponentTypeEnum.PRODUCT);
231         // DE242223
232         titanDao.commit();
233
234         if (dataModelResponse.isLeft()) {
235             Map<String, Boolean> result = new HashMap<>();
236             result.put("isValid", dataModelResponse.left().value());
237             log.debug("validation was successfully performed.");
238             return Either.left(result);
239         }
240
241         ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(dataModelResponse.right().value()));
242
243         return Either.right(responseFormat);
244     }
245
246     private Either<Boolean, ResponseFormat> validateAndUpdateProductContactsList(User user, Product product, AuditingActionEnum actionEnum) {
247         List<String> contacts = product.getContacts();
248         if (!ValidationUtils.validateListNotEmpty(contacts)) {
249             log.debug("Contacts list cannot be empty for product {}", product.getName());
250             ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.EMPTY_PRODUCT_CONTACTS_LIST);
251             componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
252             return Either.right(responseFormat);
253         }
254
255         boolean isProductCreatorInContacts = false;
256         String modifierUserId = user.getUserId();
257         for (String contact : contacts) {
258             if (contact.equals(modifierUserId)) {
259                 log.trace("modifier userId found in product contacts");
260                 isProductCreatorInContacts = true;
261                 // No need to validate for this userId - it's modifier's
262                 continue;
263             }
264             if (!ValidationUtils.validateContactId(contact)) {
265                 log.debug("Product contacts has invalid userId {} for product {}", contact, product.getName());
266                 ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INVALID_CONTACT, ComponentTypeEnum.PRODUCT.getValue());
267                 componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
268                 return Either.right(responseFormat);
269             }
270
271             User contactUser;
272             try{
273                 contactUser = validateUserExists(contact, CREATE_PRODUCT, false);
274                 validateUserRole(contactUser, contactsRoles);
275             } catch(ComponentException e){
276                 log.debug("Cannot set contact with userId {} as product contact, error: {}", contact, e.getActionStatus());
277                 ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_PRODUCT_CONTACT, contact);
278                 componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
279                 throw new ComponentException(e.getActionStatus(), e.getParams());
280             }
281         }
282
283         if (!isProductCreatorInContacts) {
284             log.debug("modifier userId {} not found in product contacts - adding it", modifierUserId);
285             contacts.add(modifierUserId);
286         }
287
288         // passed - setting all contacts userIds to lowercase
289         List<String> tempContacts = contacts.stream()
290                 .map(String::toLowerCase)
291                 .collect(Collectors.toList());
292         ValidationUtils.removeDuplicateFromList(tempContacts);
293         product.setContacts(tempContacts);
294
295         return Either.left(true);
296     }
297
298     private Either<Boolean, ResponseFormat> validateGrouping(User user, Product product, AuditingActionEnum actionEnum) {
299         List<CategoryDefinition> categories = product.getCategories();
300         if (categories == null || categories.isEmpty()) {
301             log.debug("Grouping list is empty for product: {}", product.getName());
302             return Either.left(true);
303         }
304         Map<String, Map<String, Set<String>>> nonDuplicatedCategories = new HashMap<>();
305         // remove duplicated entries
306         for (CategoryDefinition cat : categories) {
307             String catName = cat.getName();
308             if (!ValidationUtils.validateStringNotEmpty(catName)) {
309                 // error missing cat name
310                 log.debug("Missing category name for product: {}", product.getName());
311                 ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_CATEGORY, ComponentTypeEnum.PRODUCT.getValue());
312                 componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
313                 return Either.right(responseFormat);
314             }
315             Map<String, Set<String>> catEntry = nonDuplicatedCategories.get(catName);
316             if (catEntry == null) {
317                 catEntry = new HashMap<>();
318                 nonDuplicatedCategories.put(catName, catEntry);
319             }
320             List<SubCategoryDefinition> subcategories = cat.getSubcategories();
321             if (subcategories == null || subcategories.isEmpty()) {
322                 // error missing subcat for cat
323                 log.debug("Missing sub-categories for category {} in product {}", catName, product.getName());
324                 ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_SUBCATEGORY);
325                 componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
326                 return Either.right(responseFormat);
327             }
328             for (SubCategoryDefinition subcat : subcategories) {
329                 String subCatName = subcat.getName();
330                 if (!ValidationUtils.validateStringNotEmpty(subCatName)) {
331                     // error missing sub cat name for cat
332                     log.debug("Missing or empty sub-category for category {} in product {}", catName, product.getName());
333                     ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_SUBCATEGORY);
334                     componentsUtils.auditComponentAdmin(responseFormat, user, product,  actionEnum, ComponentTypeEnum.PRODUCT);
335                     return Either.right(responseFormat);
336                 }
337                 Set<String> subcatEntry = catEntry.get(subCatName);
338                 if (subcatEntry == null) {
339                     subcatEntry = new HashSet<>();
340                     catEntry.put(subCatName, subcatEntry);
341                 }
342                 List<GroupingDefinition> groupings = subcat.getGroupings();
343                 for (GroupingDefinition group : groupings) {
344                     String groupName = group.getName();
345                     if (!ValidationUtils.validateStringNotEmpty(groupName)) {
346                         // error missing grouping for sub cat name and cat
347                         log.debug("Missing or empty groupng name for sub-category: {} for categor: {} in product: {}", subCatName, catName, product.getName());
348                         ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_SUBCATEGORY);
349                         componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
350                         return Either.right(responseFormat);
351                     }
352                     if (!subcatEntry.contains(groupName)) {
353                         subcatEntry.add(groupName);
354                     } else {
355                         log.debug("Grouping: {}, already exist for category: {} and subcategory: {}", groupName, catName, subCatName);
356                     }
357                 }
358             }
359         } // for end of checking duplicated
360             // validate existence
361         Either<List<CategoryDefinition>, ActionStatus> allProductCategories = elementDao.getAllProductCategories();
362
363         if (allProductCategories.isRight()) {
364             log.debug("No product categories {}", allProductCategories.right().value());
365             ResponseFormat responseFormat = componentsUtils.getResponseFormat(allProductCategories.right().value());
366             componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
367             return Either.right(responseFormat);
368         }
369         boolean catExist;
370         // convert non-duplicated to data modeling format and update in the
371         // input object
372         List<CategoryDefinition> newCatList = new ArrayList<>();
373
374         // over all categories from request
375         for (Map.Entry<String, Map<String, Set<String>>> entry : nonDuplicatedCategories.entrySet()) {
376             catExist = false;
377             CategoryDefinition categoryDefinition = null;
378             // over all categories from Titan
379             List<CategoryDefinition> categoriesList = allProductCategories.left().value();
380             if (categoriesList != null) {
381                 for (CategoryDefinition catInDb : categoriesList) {
382                     if (entry.getKey().equals(catInDb.getName())) {
383                         catExist = true;
384                         boolean subcatExist;
385                         // copy data
386                         categoryDefinition = new CategoryDefinition(catInDb);
387                         SubCategoryDefinition subCategory = null;
388
389                         Map<String, Set<String>> subcats = entry.getValue();
390                         for (Map.Entry<String, Set<String>> subcat : subcats.entrySet()) {
391                             subcatExist = false;
392                             List<SubCategoryDefinition> subcategoriesList = catInDb.getSubcategories();
393                             if (subcategoriesList != null) {
394                                 for (SubCategoryDefinition subcatInDb : subcategoriesList) {
395                                     if (subcatInDb.getName().equals(subcat.getKey())) {
396                                         // copy data
397                                         subCategory = new SubCategoryDefinition(subcatInDb);
398                                         subcatExist = true;
399                                         Set<String> grouping = subcat.getValue();
400                                         boolean groupExist;
401                                         GroupingDefinition groupingDefinition = null;
402                                         for (String group : grouping) {
403                                             groupExist = false;
404                                             List<GroupingDefinition> groupings = subcatInDb.getGroupings();
405                                             if (groupings != null) {
406                                                 for (GroupingDefinition groupInDb : groupings) {
407                                                     if (groupInDb.getName().equals(group)) {
408                                                         groupExist = true;
409                                                         groupingDefinition = new GroupingDefinition(groupInDb);
410                                                     }
411                                                 }
412                                             }
413                                             if (!groupExist) {
414                                                 // error grouping isn't defined
415                                                 // in Titan
416                                                 ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_GROUP_ASSOCIATION, CategoryTypeEnum.GROUPING.getValue(), group);
417                                                 componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
418                                                 return Either.right(responseFormat);
419                                             }
420                                             subCategory.addGrouping(groupingDefinition);
421                                         }
422                                     }
423                                 }
424                             }
425                             if (!subcatExist) {
426                                 // error sub category isn't defined in Titan
427                                 ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_GROUP_ASSOCIATION, CategoryTypeEnum.SUBCATEGORY.getValue(), subcat.getKey());
428                                 componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
429                                 return Either.right(responseFormat);
430                             }
431                             categoryDefinition.addSubCategory(subCategory);
432                         }
433                     }
434                 }
435             }
436             if (!catExist) {
437                 // error category isn't defined in Titan
438                 ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_GROUP_ASSOCIATION, CategoryTypeEnum.CATEGORY.getValue(), entry.getKey());
439                 componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
440                 return Either.right(responseFormat);
441             }
442             newCatList.add(categoryDefinition);
443         }
444         product.setCategories(newCatList);
445         return Either.left(true);
446     }
447
448     public Either<Product, ResponseFormat> getProduct(String productId, User user) {
449         String ecompErrorContext = "Get product";
450         validateUserNotEmpty(user, ecompErrorContext);
451         validateUserExists(user, ecompErrorContext, false);
452
453         Either<Product, StorageOperationStatus> storageStatus = toscaOperationFacade.getToscaElement(productId);
454
455         if (storageStatus.isRight()) {
456             log.debug("failed to get resource by id {}", productId);
457             if (storageStatus.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
458                 // TODO check error
459                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PRODUCT_NOT_FOUND, ComponentTypeEnum.PRODUCT.getValue()));
460             } else {
461                 return Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(storageStatus.right().value()), ""));
462             }
463         }
464         return Either.left(storageStatus.left().value());
465     }
466
467     public Either<Product, ResponseFormat> deleteProduct(String productId, User user) {
468         String ecompErrorContext = "Delete product";
469         validateUserNotEmpty(user, ecompErrorContext);
470         validateUserExists(user, ecompErrorContext, false);
471
472         Either<Product, StorageOperationStatus> storageStatus = toscaOperationFacade.deleteToscaComponent(productId);
473
474         if (storageStatus.isRight()) {
475             log.debug("failed to delete resource by id {}", productId);
476             return Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(storageStatus.right().value()), ""));
477         }
478         return Either.left(storageStatus.left().value());
479     }
480
481     private Either<Boolean, ResponseFormat> validateProductFullNameAndCleanup(User user, Product product, AuditingActionEnum actionEnum) {
482         String fullName = product.getFullName();
483         if (!ValidationUtils.validateStringNotEmpty(fullName)) {
484             ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.MISSING_ONE_OF_COMPONENT_NAMES, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_FULL_NAME);
485             componentsUtils.auditComponentAdmin(errorResponse, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
486             return Either.right(errorResponse);
487         }
488
489         fullName = ValidationUtils.removeNoneUtf8Chars(fullName);
490         fullName = ValidationUtils.removeHtmlTags(fullName);
491         fullName = ValidationUtils.normaliseWhitespace(fullName);
492         fullName = ValidationUtils.stripOctets(fullName);
493
494         if (!ValidationUtils.validateProductFullNameLength(fullName)) {
495             ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_FULL_NAME);
496             componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
497             return Either.right(responseFormat);
498         }
499
500         if (!ValidationUtils.validateIsEnglish(fullName)) {
501             ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_FULL_NAME);
502             componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
503             return Either.right(responseFormat);
504         }
505
506         product.setFullName(fullName);
507         return Either.left(true);
508     }
509
510     private Either<Boolean, ResponseFormat> validateProductNameAndCleanup(User user, Product product, AuditingActionEnum actionEnum) {
511         String name = product.getName();
512         if (!ValidationUtils.validateStringNotEmpty(name)) {
513             ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_ONE_OF_COMPONENT_NAMES, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_ABBREVIATED_NAME);
514             componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
515             return Either.right(responseFormat);
516         }
517
518         // Product name is required to have same validation and normalization as
519         // category
520         if (!ValidationUtils.validateCategoryDisplayNameFormat(name)) {
521             ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_ABBREVIATED_NAME);
522             componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
523             return Either.right(responseFormat);
524         }
525
526         String normalizedName4Display = ValidationUtils.normalizeCategoryName4Display(name);
527
528         if (!ValidationUtils.validateCategoryDisplayNameLength(normalizedName4Display)) {
529             ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_ABBREVIATED_NAME);
530             componentsUtils.auditComponentAdmin(responseFormat, user, product, actionEnum, ComponentTypeEnum.PRODUCT);
531             return Either.right(responseFormat);
532         }
533
534         product.setName(normalizedName4Display);
535         String normalizedName4Uniqueness = ValidationUtils.normaliseComponentName(normalizedName4Display);
536         product.setNormalizedName(normalizedName4Uniqueness);
537
538         return Either.left(true);
539     }
540
541     // This is a workaround for a current tag--->product name behaviour, which
542     // will be changed in 1607.
543     // It was agreed with Ella on 23/2/16 that the tag validation of product
544     // will be made against the old product name (before normalization),
545     // and in 1607 US will be defined where UI will no longer pass tag of
546     // component name, and BE will add it by itself after all needed
547     // normalizations.
548     private Either<Boolean, ResponseFormat> validateTagsListAndRemoveDuplicates(User user, Product product, String oldProductName, AuditingActionEnum actionEnum) {
549         List<String> tagsList = product.getTags();
550         validateComponentTags(tagsList, oldProductName, ComponentTypeEnum.PRODUCT, user, product, actionEnum);
551         ValidationUtils.removeDuplicateFromList(tagsList);
552         return Either.left(true);
553     }
554
555     @Override
556     public void setDeploymentArtifactsPlaceHolder(org.openecomp.sdc.be.model.Component component, User user) {
557
558     }
559
560     public Either<Product, ResponseFormat> updateProductMetadata(String productId, Product updatedProduct, User user) {
561         ComponentTypeEnum typeEnum = ComponentTypeEnum.PRODUCT;
562         user = validateUser(user, "Update Product", updatedProduct, null, false);
563         // validate user role
564         validateUserRole(user, updatedProduct, updateRoles, null, null);
565         if (updatedProduct == null) {
566             log.debug("Invalid product json. Check product servlet log for updateProduct entry params");
567             ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
568             return Either.right(responseFormat);
569         }
570
571         Either<Product, StorageOperationStatus> storageStatus = toscaOperationFacade.getToscaElement(productId);
572         if (storageStatus.isRight()) {
573             if (storageStatus.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
574                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PRODUCT_NOT_FOUND, ComponentTypeEnum.PRODUCT.name().toLowerCase()));
575             }
576             return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(storageStatus.right().value(), typeEnum), ""));
577         }
578
579         Product currentProduct = storageStatus.left().value();
580
581         if (!ComponentValidationUtils.canWorkOnComponent(productId, toscaOperationFacade, user.getUserId())) {
582             log.info("Restricted operation for user: {}, on product: {}" , user.getUserId(), currentProduct.getCreatorUserId());
583             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
584         }
585
586         Either<Product, ResponseFormat> validationRsponse = validateAndUpdateProductMetadata(user, currentProduct, updatedProduct);
587         if (validationRsponse.isRight()) {
588             log.info("product update metadata: validations field.");
589             return validationRsponse;
590         }
591
592         Product productToUpdate = validationRsponse.left().value();
593         // lock resource
594         Either<Boolean, ResponseFormat> lockResult = lockComponent(currentProduct.getUniqueId(), currentProduct, "Update Product Metadata");
595         if (lockResult.isRight()) {
596             return Either.right(lockResult.right().value());
597         }
598         try {
599             Either<Product, StorageOperationStatus> updateResponse = toscaOperationFacade.updateToscaElement(productToUpdate);
600             if (updateResponse.isRight()) {
601                 toscaOperationFacade.rollback();
602                 log.debug("failed to update product {}", productToUpdate.getUniqueId());
603                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
604             }
605             toscaOperationFacade.commit();
606             return Either.left(updateResponse.left().value());
607         } finally {
608             graphLockOperation.unlockComponent(productId, NodeTypeEnum.Product);
609         }
610     }
611
612     private Either<Product, ResponseFormat> validateAndUpdateProductMetadata(User user, Product currentProduct, Product updatedProduct) {
613
614         boolean hasBeenCertified = ValidationUtils.hasBeenCertified(currentProduct.getVersion());
615         Either<Boolean, ResponseFormat> response = validateAndUpdateProductName(user, currentProduct, updatedProduct);
616         if (response.isRight()) {
617             ResponseFormat errorResponse = response.right().value();
618             return Either.right(errorResponse);
619         }
620
621         response = validateAndUpdateFullName(user, currentProduct, updatedProduct);
622         if (response.isRight()) {
623             ResponseFormat errorResponse = response.right().value();
624             return Either.right(errorResponse);
625         }
626
627         response = validateAndUpdateDescription(user, currentProduct, updatedProduct, null);
628         if (response.isRight()) {
629             ResponseFormat errorResponse = response.right().value();
630             return Either.right(errorResponse);
631         }
632
633         response = validateAndUpdateCategory(user, currentProduct, updatedProduct);
634         if (response.isRight()) {
635             ResponseFormat errorResponse = response.right().value();
636             return Either.right(errorResponse);
637         }
638
639         response = validateAndUpdateContactList(user, currentProduct, updatedProduct);
640         if (response.isRight()) {
641             ResponseFormat errorResponse = response.right().value();
642             return Either.right(errorResponse);
643         }
644
645         response = validateAndUpdateTags(user, currentProduct, updatedProduct);
646         if (response.isRight()) {
647             ResponseFormat errorResponse = response.right().value();
648             return Either.right(errorResponse);
649         }
650
651         response = validateAndUpdateProjectCode(user, currentProduct, updatedProduct);
652         if (response.isRight()) {
653             ResponseFormat errorResponse = response.right().value();
654             return Either.right(errorResponse);
655         }
656
657         if (updatedProduct.getIsActive() != null) {
658             currentProduct.setIsActive(updatedProduct.getIsActive());
659         }
660
661         response = validateAndUpdateIcon(user, currentProduct, updatedProduct, hasBeenCertified);
662         if (response.isRight()) {
663             ResponseFormat errorResponse = response.right().value();
664             return Either.right(errorResponse);
665         }
666
667         String currentInvariantUuid = currentProduct.getInvariantUUID();
668         String updatedInvariantUuid = updatedProduct.getInvariantUUID();
669
670         if ((updatedInvariantUuid != null) && (!updatedInvariantUuid.equals(currentInvariantUuid))) {
671             log.warn("Product invariant UUID is automatically set and cannot be updated");
672             updatedProduct.setInvariantUUID(currentInvariantUuid);
673         }
674         return Either.left(currentProduct);
675
676     }
677
678     private Either<Boolean, ResponseFormat> validateAndUpdateProductName(User user, Product currentProduct, Product updatedProduct) {
679         String updatedProductName = updatedProduct.getName();
680         String tags;
681         String currentProductName = currentProduct.getName();
682         if (updatedProductName != null) {
683             Either<Boolean, ResponseFormat> validatProductNameResponse = validateProductNameAndCleanup(user, updatedProduct, null);
684             if (validatProductNameResponse.isRight()) {
685                 ResponseFormat errorRespons = validatProductNameResponse.right().value();
686                 return Either.right(errorRespons);
687             }
688             updatedProductName = updatedProduct.getName();
689             if (!currentProductName.equals(updatedProductName)) {
690                 Either<Boolean, ResponseFormat> productNameUniquenessValidation = validateComponentNameUnique(user, updatedProduct, null);
691                 if (productNameUniquenessValidation.isRight()) {
692                     return productNameUniquenessValidation;
693                 }
694                 currentProduct.setName(updatedProductName);
695                 tags = updatedProductName;
696                 updatedProductName = ValidationUtils.normalizeCategoryName4Display(updatedProductName);
697                 currentProduct.getComponentMetadataDefinition().getMetadataDataDefinition().setNormalizedName(ValidationUtils.normaliseComponentName(updatedProductName));
698                 List<String> updatedTags = updatedProduct.getTags();
699                 // As discussed with Ella currently (1604) we are not removing
700                 // the old name from tags.
701                 if (updatedTags == null) {
702                     updatedTags = currentProduct.getTags();
703                 }
704                 updatedTags.add(tags);
705             }
706         }
707         return Either.left(true);
708     }
709
710     private Either<Boolean, ResponseFormat> validateAndUpdateFullName(User user, Product currentProduct, Product updatedProduct) {
711         String updatedProductName = updatedProduct.getFullName();
712         String currentProductName = currentProduct.getFullName();
713         if (updatedProductName != null && !currentProductName.equals(updatedProductName)) {
714             Either<Boolean, ResponseFormat> validatProductNameResponse = validateProductFullNameAndCleanup(user, updatedProduct, null);
715             if (validatProductNameResponse.isRight()) {
716                 ResponseFormat errorRespons = validatProductNameResponse.right().value();
717                 return Either.right(errorRespons);
718             }
719             currentProduct.setFullName(updatedProduct.getFullName());
720         }
721         return Either.left(true);
722     }
723
724     private Either<Boolean, ResponseFormat> validateAndUpdateCategory(User user, Product currentProduct, Product updatedProduct) {
725
726         Either<Boolean, ResponseFormat> validateCategoryResponse = validateGrouping(user, updatedProduct, null);
727         if (validateCategoryResponse.isRight()) {
728             ResponseFormat errorResponse = validateCategoryResponse.right().value();
729             return Either.right(errorResponse);
730         }
731
732         List<CategoryDefinition> categoryUpdated = updatedProduct.getCategories();
733         if (categoryUpdated != null) {
734             currentProduct.setCategories(categoryUpdated);
735         }
736         return Either.left(true);
737     }
738
739     private Either<Boolean, ResponseFormat> validateAndUpdateContactList(User user, Product currentProduct, Product updatedProduct) {
740         List<String> updatedContacts = updatedProduct.getContacts();
741         List<String> currentContacts = currentProduct.getContacts();
742         if (updatedContacts != null) {
743             if (!(currentContacts.containsAll(updatedContacts) && updatedContacts.containsAll(currentContacts))) {
744                 Either<Boolean, ResponseFormat> validatResponse = validateAndUpdateProductContactsList(user, updatedProduct, null);
745                 if (validatResponse.isRight()) {
746                     ResponseFormat errorRespons = validatResponse.right().value();
747                     return Either.right(errorRespons);
748                 }
749                 currentProduct.setContacts(updatedProduct.getContacts());
750             }
751         }
752         return Either.left(true);
753     }
754
755     private Either<Boolean, ResponseFormat> validateAndUpdateTags(User user, Product currentProduct, Product updatedProduct) {
756         List<String> tagsUpdated = updatedProduct.getTags();
757         List<String> tagsCurrent = currentProduct.getTags();
758         if (tagsUpdated != null) {
759             if (!(tagsCurrent.containsAll(tagsUpdated) && tagsUpdated.containsAll(tagsCurrent))) {
760                 Either<Boolean, ResponseFormat> validatResponse = validateTagsListAndRemoveDuplicates(user, updatedProduct, currentProduct.getName(), null);
761                 if (validatResponse.isRight()) {
762                     ResponseFormat errorRespons = validatResponse.right().value();
763                     return Either.right(errorRespons);
764                 }
765                 currentProduct.setTags(updatedProduct.getTags());
766             }
767         }
768         return Either.left(true);
769     }
770
771     @Override
772     public Either<List<String>, ResponseFormat> deleteMarkedComponents() {
773         // markAsDeleted isnt implemented yet
774         return Either.left(new ArrayList<>());
775     }
776
777     @Override
778     protected boolean validateTagPattern(String tag) {
779         return ValidationUtils.validateCategoryDisplayNameFormat(tag);
780     }
781
782     public Either<Product, ResponseFormat> getProductByNameAndVersion(String productName, String productVersion, String userId) {
783         validateUserExists(userId, "get Service By Name And Version", false);
784         Either<Product, StorageOperationStatus> storageStatus = toscaOperationFacade.getComponentByNameAndVersion(ComponentTypeEnum.PRODUCT, productName, productVersion);
785         if (storageStatus.isRight()) {
786             log.debug("failed to get service by name {} and version {}", productName, productVersion);
787             return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(storageStatus.right().value(), ComponentTypeEnum.PRODUCT), productName));
788         }
789         Product product = storageStatus.left().value();
790         return Either.left(product);
791     }
792
793     @Override
794     public ComponentInstanceBusinessLogic getComponentInstanceBL() {
795         return componentInstanceBusinessLogic;
796     }
797
798     @Override
799     public Either<List<ComponentInstance>, ResponseFormat> getComponentInstancesFilteredByPropertiesAndInputs(String componentId, String userId) {
800         return null;
801     }
802
803     public ICacheMangerOperation getCacheManagerOperation() {
804         return cacheManagerOperation;
805     }
806
807     public void setCacheManagerOperation(ICacheMangerOperation cacheManagerOperation) {
808         this.cacheManagerOperation = cacheManagerOperation;
809     }
810
811     @Override
812     public Either<UiComponentDataTransfer, ResponseFormat> getUiComponentDataTransferByComponentId(String componentId,
813             List<String> dataParamsToReturn) {
814         // TODO Auto-generated method stub
815         return null;
816     }
817
818 }