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