Add support for simple yaml profile 1.2
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / ResourceImportManager.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.apache.commons.codec.binary.Base64;
27 import org.apache.commons.lang3.StringUtils;
28 import org.apache.commons.lang3.tuple.ImmutablePair;
29 import org.openecomp.sdc.be.auditing.api.AuditEventFactory;
30 import org.openecomp.sdc.be.auditing.impl.AuditingManager;
31 import org.openecomp.sdc.be.auditing.impl.resourceadmin.AuditImportResourceAdminEventFactory;
32 import org.openecomp.sdc.be.components.csar.CsarInfo;
33 import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic.ArtifactOperationEnum;
34 import org.openecomp.sdc.be.components.impl.ImportUtils.Constants;
35 import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
36 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
37 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
38 import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction;
39 import org.openecomp.sdc.be.config.BeEcompErrorManager;
40 import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
41 import org.openecomp.sdc.be.dao.api.ActionStatus;
42 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
43 import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields;
44 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
45 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
46 import org.openecomp.sdc.be.impl.ComponentsUtils;
47 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
48 import org.openecomp.sdc.be.model.ArtifactDefinition;
49 import org.openecomp.sdc.be.model.CapabilityDefinition;
50 import org.openecomp.sdc.be.model.ComponentInstanceProperty;
51 import org.openecomp.sdc.be.model.InterfaceDefinition;
52 import org.openecomp.sdc.be.model.LifecycleStateEnum;
53 import org.openecomp.sdc.be.model.PropertyDefinition;
54 import org.openecomp.sdc.be.model.RequirementDefinition;
55 import org.openecomp.sdc.be.model.Resource;
56 import org.openecomp.sdc.be.model.UploadResourceInfo;
57 import org.openecomp.sdc.be.model.User;
58 import org.openecomp.sdc.be.model.category.CategoryDefinition;
59 import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
60 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade;
61 import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
62 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
63 import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation;
64 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
65 import org.openecomp.sdc.be.resources.data.auditing.model.CommonAuditData;
66 import org.openecomp.sdc.be.resources.data.auditing.model.ResourceCommonInfo;
67 import org.openecomp.sdc.be.resources.data.auditing.model.ResourceVersionInfo;
68 import org.openecomp.sdc.be.utils.TypeUtils;
69 import org.openecomp.sdc.common.log.wrappers.Logger;
70 import org.openecomp.sdc.common.util.ThreadLocalsHolder;
71 import org.openecomp.sdc.common.util.ValidationUtils;
72 import org.openecomp.sdc.exception.ResponseFormat;
73 import org.springframework.beans.factory.annotation.Autowired;
74 import org.springframework.stereotype.Component;
75 import org.springframework.web.context.WebApplicationContext;
76 import org.yaml.snakeyaml.Yaml;
77
78 import javax.servlet.ServletContext;
79 import java.util.ArrayList;
80 import java.util.Arrays;
81 import java.util.HashMap;
82 import java.util.HashSet;
83 import java.util.Iterator;
84 import java.util.List;
85 import java.util.Map;
86 import java.util.Map.Entry;
87 import java.util.Set;
88 import java.util.function.Function;
89 import java.util.regex.Pattern;
90 import java.util.stream.Collectors;
91
92 @Component("resourceImportManager")
93 public class ResourceImportManager {
94     static final Pattern PROPERTY_NAME_PATTERN_IGNORE_LENGTH = Pattern.compile("[\\w\\-\\_\\d\\:]+");
95
96     private ServletContext servletContext;
97
98     private AuditingManager auditingManager;
99     private ResourceBusinessLogic resourceBusinessLogic;
100     private IGraphLockOperation graphLockOperation;
101     protected ToscaOperationFacade toscaOperationFacade;
102
103     protected final ComponentsUtils componentsUtils;
104     private final CapabilityTypeOperation capabilityTypeOperation;
105
106     private ResponseFormatManager responseFormatManager;
107
108     private static final Logger log = Logger.getLogger(ResourceImportManager.class);
109
110     @Autowired
111     public ResourceImportManager(ComponentsUtils componentsUtils, CapabilityTypeOperation capabilityTypeOperation) {
112         this.componentsUtils = componentsUtils;
113         this.capabilityTypeOperation = capabilityTypeOperation;
114     }
115
116     @Autowired
117     public void setToscaOperationFacade(ToscaOperationFacade toscaOperationFacade) {
118         this.toscaOperationFacade = toscaOperationFacade;
119     }
120
121     public ImmutablePair<Resource, ActionStatus> importNormativeResource(String resourceYml, UploadResourceInfo resourceMetaData, User creator, boolean createNewVersion, boolean needLock) {
122
123         LifecycleChangeInfoWithAction lifecycleChangeInfo = new LifecycleChangeInfoWithAction();
124         lifecycleChangeInfo.setUserRemarks("certification on import");
125         Function<Resource, Boolean> validator = resource -> resourceBusinessLogic.validatePropertiesDefaultValues(resource);
126
127         return importCertifiedResource(resourceYml, resourceMetaData, creator, validator, lifecycleChangeInfo, false, createNewVersion, needLock, null, null, false, null, null, false);
128     }
129
130     public ImmutablePair<Resource, ActionStatus> importNormativeResourceFromCsar(String resourceYml, UploadResourceInfo resourceMetaData, User creator, boolean createNewVersion, boolean needLock) {
131
132         LifecycleChangeInfoWithAction lifecycleChangeInfo = new LifecycleChangeInfoWithAction();
133         lifecycleChangeInfo.setUserRemarks("certification on import");
134         Function<Resource, Boolean> validator = resource -> resourceBusinessLogic.validatePropertiesDefaultValues(resource);
135
136         return importCertifiedResource(resourceYml, resourceMetaData, creator, validator, lifecycleChangeInfo, false, createNewVersion, needLock, null, null, false, null, null, false);
137     }
138
139     public ImmutablePair<Resource, ActionStatus> importCertifiedResource(String resourceYml, UploadResourceInfo resourceMetaData, User creator,
140                                                                          Function<Resource, Boolean> validationFunction,
141                                                                          LifecycleChangeInfoWithAction lifecycleChangeInfo, boolean isInTransaction, boolean createNewVersion, boolean needLock, Map<ArtifactOperationEnum, List<ArtifactDefinition>> nodeTypeArtifactsToHandle, List<ArtifactDefinition> nodeTypesNewCreatedArtifacts, boolean forceCertificationAllowed, CsarInfo csarInfo, String nodeName, boolean isNested) {
142         Resource resource = new Resource();
143         ImmutablePair<Resource, ActionStatus> responsePair = new ImmutablePair<>(resource, ActionStatus.CREATED);
144         Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> response = Either.left(responsePair);
145
146         String latestCertifiedResourceId = null;
147         try {
148             boolean shouldBeCertified = nodeTypeArtifactsToHandle == null || nodeTypeArtifactsToHandle.isEmpty();
149             setConstantMetaData(resource, shouldBeCertified);
150             setMetaDataFromJson(resourceMetaData, resource);
151
152             populateResourceFromYaml(resourceYml, resource);
153
154             Boolean isValidResource = validationFunction.apply(resource);
155                 if (!createNewVersion) {
156                     Either<Resource, StorageOperationStatus> latestByName = toscaOperationFacade.getLatestByName(resource.getName());
157                     if (latestByName.isLeft()) {
158                         throw new ByActionStatusComponentException(ActionStatus.COMPONENT_NAME_ALREADY_EXIST, resource.getName());
159                     }
160                 }
161                 resource = resourceBusinessLogic.createOrUpdateResourceByImport(resource, creator, true, isInTransaction, needLock, csarInfo, nodeName, isNested).left;
162                 Resource changeStateResponse;
163
164                 if (nodeTypeArtifactsToHandle != null && !nodeTypeArtifactsToHandle.isEmpty()) {
165                     Either<List<ArtifactDefinition>, ResponseFormat> handleNodeTypeArtifactsRes =
166                             resourceBusinessLogic.handleNodeTypeArtifacts(resource, nodeTypeArtifactsToHandle, nodeTypesNewCreatedArtifacts, creator, isInTransaction, false);
167                     if (handleNodeTypeArtifactsRes.isRight()) {
168                         //TODO: should be used more correct action
169                         throw new ByActionStatusComponentException(ActionStatus.GENERAL_ERROR);
170                     }
171                 }
172                 latestCertifiedResourceId = getLatestCertifiedResourceId(resource);
173                 changeStateResponse = resourceBusinessLogic.propagateStateToCertified(creator, resource, lifecycleChangeInfo, isInTransaction, needLock, forceCertificationAllowed);
174                 responsePair = new ImmutablePair<>(changeStateResponse, response.left()
175                         .value().right);
176         }
177         catch (RuntimeException e) {
178             handleImportResourceException(resourceMetaData, creator, true, e);
179         }
180         finally {
181             if (latestCertifiedResourceId != null && needLock) {
182                 log.debug("unlock resource {}", latestCertifiedResourceId);
183                 graphLockOperation.unlockComponent(latestCertifiedResourceId, NodeTypeEnum.Resource);
184             }
185         }
186
187         return responsePair;
188     }
189
190     private ResponseFormat getResponseFormatFromComponentException(RuntimeException e) {
191         if(e instanceof ComponentException){
192             return ((ComponentException) e).getResponseFormat() == null ?
193                     componentsUtils.getResponseFormat(((ComponentException) e).getActionStatus(), ((ComponentException) e).getParams()) :
194                     ((ComponentException) e).getResponseFormat();
195         }
196         return null;
197     }
198
199     private String getLatestCertifiedResourceId(Resource resource) {
200         Map<String, String> allVersions = resource.getAllVersions();
201         Double latestCertifiedVersion = 0.0;
202         if (allVersions != null) {
203             for (String version : allVersions.keySet()) {
204                 Double dVersion = Double.valueOf(version);
205                 if ((dVersion > latestCertifiedVersion) && (version.endsWith(".0"))) {
206                     latestCertifiedVersion = dVersion;
207                 }
208             }
209             return allVersions.get(String.valueOf(latestCertifiedVersion));
210         }
211         else {
212             return null;
213         }
214     }
215
216     public void populateResourceMetadata(UploadResourceInfo resourceMetaData, Resource resource) {
217         if (resource != null && resourceMetaData != null) {
218             resource.setDescription(resourceMetaData.getDescription());
219             resource.setTags(resourceMetaData.getTags());
220             resource.setCategories(resourceMetaData.getCategories());
221             resource.setContactId(resourceMetaData.getContactId());
222             resource.setName(resourceMetaData.getName());
223             resource.setIcon(resourceMetaData.getResourceIconPath());
224             resource.setResourceVendorModelNumber(resourceMetaData.getResourceVendorModelNumber());
225             resource.setResourceType(ResourceTypeEnum.valueOf(resourceMetaData.getResourceType()));
226             if (resourceMetaData.getVendorName() != null) {
227                 resource.setVendorName(resourceMetaData.getVendorName());
228             }
229             if (resourceMetaData.getVendorRelease() != null) {
230                 resource.setVendorRelease(resourceMetaData.getVendorRelease());
231             }
232         }
233     }
234
235     public ImmutablePair<Resource, ActionStatus> importUserDefinedResource(String resourceYml, UploadResourceInfo resourceMetaData, User creator, boolean isInTransaction) {
236
237         Resource resource = new Resource();
238         ImmutablePair<Resource, ActionStatus> responsePair = new ImmutablePair<>(resource, ActionStatus.CREATED);
239
240         try {
241             setMetaDataFromJson(resourceMetaData, resource);
242
243             populateResourceFromYaml(resourceYml, resource);
244
245             // currently import VF isn't supported. In future will be supported
246             // import VF only with CSAR file!!
247             if (ResourceTypeEnum.VF == resource.getResourceType()) {
248                 log.debug("Now import VF isn't supported. It will be supported in future with CSAR file only");
249                 throw new ByActionStatusComponentException(ActionStatus.RESTRICTED_OPERATION);
250             }
251
252             resourceBusinessLogic.validateDerivedFromNotEmpty(creator, resource, AuditingActionEnum.CREATE_RESOURCE);
253             Boolean validatePropertiesTypes = resourceBusinessLogic.validatePropertiesDefaultValues(resource);
254
255             responsePair = resourceBusinessLogic.createOrUpdateResourceByImport(resource, creator,
256                         false, isInTransaction, true, null, null, false);
257
258         }
259         catch (RuntimeException e) {
260             handleImportResourceException(resourceMetaData, creator, false, e);
261         }
262         return responsePair;
263
264     }
265
266     void populateResourceFromYaml(String resourceYml, Resource resource) {
267         @SuppressWarnings("unchecked")
268 //        Either<Boolean, ResponseFormat> eitherResult = Either.left(true);
269         Object ymlObj = new Yaml().load(resourceYml);
270         if (ymlObj instanceof Map) {
271             Map<String, Object> toscaJsonAll = (Map<String, Object>) ymlObj;
272             Map<String, Object> toscaJson = toscaJsonAll;
273
274             // Checks if exist and builds the node_types map
275             if (toscaJsonAll.containsKey(TypeUtils.ToscaTagNamesEnum.NODE_TYPES.getElementName()) && resource.getResourceType() != ResourceTypeEnum.CVFC) {
276                 toscaJson = new HashMap<>();
277                 toscaJson.put(TypeUtils.ToscaTagNamesEnum.NODE_TYPES.getElementName(), toscaJsonAll.get(TypeUtils.ToscaTagNamesEnum.NODE_TYPES.getElementName()));
278             }
279             // Derived From
280             Resource parentResource = setDerivedFrom(toscaJson, resource);
281             if (StringUtils.isEmpty(resource.getToscaResourceName())) {
282                 setToscaResourceName(toscaJson, resource);
283             }
284             setAttributes(toscaJson, resource);
285             setCapabilities(toscaJson, resource, parentResource);
286             setProperties(toscaJson, resource);
287             setRequirements(toscaJson, resource, parentResource);
288             setInterfaceLifecycle(toscaJson, resource);
289         }
290         else {
291             throw new ByActionStatusComponentException(ActionStatus.GENERAL_ERROR);
292         }
293
294     }
295
296     private void setToscaResourceName(Map<String, Object> toscaJson, Resource resource) {
297         Either<Map<String, Object>, ResultStatusEnum> toscaElement = ImportUtils.findFirstToscaMapElement(toscaJson, TypeUtils.ToscaTagNamesEnum.NODE_TYPES);
298         if (toscaElement.isLeft() || toscaElement.left().value().size() == 1) {
299             String toscaResourceName = toscaElement.left().value().keySet().iterator().next();
300             resource.setToscaResourceName(toscaResourceName);
301         }
302     }
303
304     private void setInterfaceLifecycle(Map<String, Object> toscaJson, Resource resource) {
305         Either<Map<String, Object>, ResultStatusEnum> toscaInterfaces = ImportUtils.findFirstToscaMapElement(toscaJson, TypeUtils.ToscaTagNamesEnum.INTERFACES);
306         if (toscaInterfaces.isLeft()) {
307             Map<String, Object> jsonInterfaces = toscaInterfaces.left().value();
308             Map<String, InterfaceDefinition> moduleInterfaces = new HashMap<>();
309             Iterator<Entry<String, Object>> interfacesNameValue = jsonInterfaces.entrySet().iterator();
310             while (interfacesNameValue.hasNext()) {
311                 Entry<String, Object> interfaceNameValue = interfacesNameValue.next();
312                 Either<InterfaceDefinition, ResultStatusEnum> eitherInterface = createModuleInterface(interfaceNameValue
313                         .getValue());
314                 if (eitherInterface.isRight()) {
315                     log.info("error when creating interface:{}, for resource:{}", interfaceNameValue.getKey(), resource.getName());
316                 }
317                 else {
318                     moduleInterfaces.put(interfaceNameValue.getKey(), eitherInterface.left().value());
319                 }
320
321             }
322             if (moduleInterfaces.size() > 0) {
323                 resource.setInterfaces(moduleInterfaces);
324             }
325         }
326     }
327
328     private Either<InterfaceDefinition, ResultStatusEnum> createModuleInterface(Object interfaceJson) {
329         InterfaceDefinition interf = new InterfaceDefinition();
330         Either<InterfaceDefinition, ResultStatusEnum> result = Either.left(interf);
331
332         try {
333             if (interfaceJson instanceof String) {
334                 String requirementJsonString = (String) interfaceJson;
335                 interf.setType(requirementJsonString);
336             }
337             else if (interfaceJson instanceof Map) {
338                 Map<String, Object> requirementJsonMap = (Map<String, Object>) interfaceJson;
339                 if (requirementJsonMap.containsKey(TypeUtils.ToscaTagNamesEnum.TYPE.getElementName())) {
340                     String type = (String) requirementJsonMap.get(TypeUtils.ToscaTagNamesEnum.TYPE.getElementName());
341                     interf.setType(type);
342                     interf.setUniqueId(type.toLowerCase());
343                 }
344             }
345             else {
346                 result = Either.right(ResultStatusEnum.GENERAL_ERROR);
347             }
348
349         }
350         catch (Exception e) {
351             BeEcompErrorManager.getInstance().logBeSystemError("Import Resource- create interface");
352             log.debug("error when creating interface, message:{}", e.getMessage(), e);
353             result = Either.right(ResultStatusEnum.GENERAL_ERROR);
354         }
355
356         return result;
357     }
358
359     private void setRequirements(Map<String, Object> toscaJson, Resource resource, Resource parentResource) {// Note that parentResource can be null
360         Either<List<Object>, ResultStatusEnum> toscaRequirements = ImportUtils.findFirstToscaListElement(toscaJson, TypeUtils.ToscaTagNamesEnum.REQUIREMENTS);
361         if (toscaRequirements.isLeft()) {
362             List<Object> jsonRequirements = toscaRequirements.left().value();
363             Map<String, List<RequirementDefinition>> moduleRequirements = new HashMap<>();
364             // Checking for name duplication
365             Set<String> reqNames = new HashSet<>();
366             // Getting flattened list of capabilities of parent node - cap name
367             // to cap type
368             Map<String, String> reqName2TypeMap = getReqName2Type(parentResource);
369             for (Object jsonRequirementObj : jsonRequirements) {
370                 // Requirement
371                 Map<String, Object> requirementJsonWrapper = (Map<String, Object>) jsonRequirementObj;
372                 String requirementName = requirementJsonWrapper.keySet().iterator().next();
373                 String reqNameLowerCase = requirementName.toLowerCase();
374                 if (reqNames.contains(reqNameLowerCase)) {
375                     log.debug("More than one requirement with same name {} (case-insensitive) in imported TOSCA file is invalid", reqNameLowerCase);
376                     throw new ByActionStatusComponentException(ActionStatus.IMPORT_DUPLICATE_REQ_CAP_NAME, "requirement", reqNameLowerCase);
377                 }
378                 reqNames.add(reqNameLowerCase);
379                 RequirementDefinition requirementDef = createRequirementFromImportFile(requirementJsonWrapper
380                         .get(requirementName));
381                 requirementDef.setName(requirementName);
382                 if (moduleRequirements.containsKey(requirementDef.getCapability())) {
383                     moduleRequirements.get(requirementDef.getCapability()).add(requirementDef);
384                 }
385                 else {
386                     List<RequirementDefinition> list = new ArrayList<>();
387                     list.add(requirementDef);
388                     moduleRequirements.put(requirementDef.getCapability(), list);
389                 }
390
391                 // Validating against req/cap of "derived from" node
392                 Boolean validateVsParentCap = validateCapNameVsDerived(reqName2TypeMap, requirementDef
393                         .getCapability(), requirementDef.getName());
394                 if (!validateVsParentCap) {
395                     log.debug("Requirement with name {} already exists in parent {}", requirementDef.getName(), parentResource
396                             .getName());
397                     throw new ByActionStatusComponentException(ActionStatus.IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED, "requirement", requirementDef
398                             .getName()
399                             .toLowerCase(), parentResource.getName());
400                 }
401             }
402             if (moduleRequirements.size() > 0) {
403                 resource.setRequirements(moduleRequirements);
404             }
405
406         }
407    }
408
409     private RequirementDefinition createRequirementFromImportFile(Object requirementJson) {
410         RequirementDefinition requirement = new RequirementDefinition();
411
412         if (requirementJson instanceof String) {
413             String requirementJsonString = (String) requirementJson;
414             requirement.setCapability(requirementJsonString);
415         }
416         else if (requirementJson instanceof Map) {
417             Map<String, Object> requirementJsonMap = (Map<String, Object>) requirementJson;
418             if (requirementJsonMap.containsKey(TypeUtils.ToscaTagNamesEnum.CAPABILITY.getElementName())) {
419                 requirement.setCapability((String) requirementJsonMap.get(TypeUtils.ToscaTagNamesEnum.CAPABILITY.getElementName()));
420             }
421
422             if (requirementJsonMap.containsKey(TypeUtils.ToscaTagNamesEnum.NODE.getElementName())) {
423                 requirement.setNode((String) requirementJsonMap.get(TypeUtils.ToscaTagNamesEnum.NODE.getElementName()));
424             }
425
426             if (requirementJsonMap.containsKey(TypeUtils.ToscaTagNamesEnum.RELATIONSHIP.getElementName())) {
427                 requirement.setRelationship((String) requirementJsonMap.get(TypeUtils.ToscaTagNamesEnum.RELATIONSHIP.getElementName()));
428             }
429             if (requirementJsonMap.containsKey(TypeUtils.ToscaTagNamesEnum.OCCURRENCES.getElementName())) {
430                 List<Object> occurrencesList = (List) requirementJsonMap.get(TypeUtils.ToscaTagNamesEnum.OCCURRENCES.getElementName());
431                 validateOccurrences(occurrencesList);
432                 requirement.setMinOccurrences(occurrencesList.get(0).toString());
433                 requirement.setMaxOccurrences(occurrencesList.get(1).toString());
434             }
435         }
436         else {
437             throw new ByActionStatusComponentException(ActionStatus.INVALID_YAML);
438         }
439         return requirement;
440     }
441
442     private void setProperties(Map<String, Object> toscaJson, Resource resource) {
443         Map<String, Object> reducedToscaJson = new HashMap<>(toscaJson);
444         ImportUtils.removeElementFromJsonMap(reducedToscaJson, "capabilities");
445         Either<Map<String, PropertyDefinition>, ResultStatusEnum> properties = ImportUtils.getProperties(reducedToscaJson);
446         if (properties.isLeft()) {
447             List<PropertyDefinition> propertiesList = new ArrayList<>();
448             Map<String, PropertyDefinition> value = properties.left().value();
449             if (value != null) {
450                 for (Entry<String, PropertyDefinition> entry : value.entrySet()) {
451                     String name = entry.getKey();
452                     if (!PROPERTY_NAME_PATTERN_IGNORE_LENGTH.matcher(name).matches()) {
453                         log.debug("The property with invalid name {} occured upon import resource {}. ", name, resource.getName());
454                         throw new ByActionStatusComponentException(componentsUtils.convertFromResultStatusEnum(ResultStatusEnum.INVALID_PROPERTY_NAME, JsonPresentationFields.PROPERTY));
455                     }
456                     PropertyDefinition propertyDefinition = entry.getValue();
457                     propertyDefinition.setName(name);
458                     propertiesList.add(propertyDefinition);
459                 }
460             }
461             resource.setProperties(propertiesList);
462         }
463         else if (properties.right().value() != ResultStatusEnum.ELEMENT_NOT_FOUND) {
464             throw new ByActionStatusComponentException(componentsUtils.convertFromResultStatusEnum(properties
465                     .right()
466                     .value(), JsonPresentationFields.PROPERTY));
467         }
468     }
469
470     private ResultStatusEnum setAttributes(Map<String, Object> toscaJson, Resource resource) {
471         ResultStatusEnum result = ResultStatusEnum.OK;
472         Either<Map<String, PropertyDefinition>, ResultStatusEnum> attributes = ImportUtils.getAttributes(toscaJson);
473         if (attributes.isLeft()) {
474             List<PropertyDefinition> attributeList = new ArrayList<>();
475             Map<String, PropertyDefinition> value = attributes.left().value();
476             if (value != null) {
477                 for (Entry<String, PropertyDefinition> entry : value.entrySet()) {
478                     String name = entry.getKey();
479                     PropertyDefinition attributeDef = entry.getValue();
480                     attributeDef.setName(name);
481                     attributeList.add(attributeDef);
482                 }
483             }
484             resource.setAttributes(attributeList);
485         }
486         else {
487             result = attributes.right().value();
488         }
489         return result;
490     }
491
492     private Resource setDerivedFrom(Map<String, Object> toscaJson, Resource resource) {
493         Either<String, ResultStatusEnum> toscaDerivedFromElement = ImportUtils.findFirstToscaStringElement(toscaJson, TypeUtils.ToscaTagNamesEnum.DERIVED_FROM);
494         Resource derivedFromResource = null;
495         if (toscaDerivedFromElement.isLeft()) {
496             String derivedFrom = toscaDerivedFromElement.left().value();
497             log.debug("Derived from TOSCA name is {}", derivedFrom);
498             resource.setDerivedFrom(Arrays.asList(new String[]{derivedFrom}));
499             Either<Resource, StorageOperationStatus> latestByToscaResourceName = toscaOperationFacade.getLatestByToscaResourceName(derivedFrom);
500
501             if (latestByToscaResourceName.isRight()) {
502                 StorageOperationStatus operationStatus = latestByToscaResourceName.right().value();
503                 if (operationStatus == StorageOperationStatus.NOT_FOUND) {
504                     operationStatus = StorageOperationStatus.PARENT_RESOURCE_NOT_FOUND;
505                 }
506                 log.debug("Error when fetching parent resource {}, error: {}", derivedFrom, operationStatus);
507                 ActionStatus convertFromStorageResponse = componentsUtils.convertFromStorageResponse(operationStatus);
508                 BeEcompErrorManager.getInstance()
509                                    .logBeComponentMissingError("Import TOSCA YAML", "resource", derivedFrom);
510                 throw new ByActionStatusComponentException(convertFromStorageResponse, derivedFrom);
511             }
512             derivedFromResource = latestByToscaResourceName.left().value();
513         }
514         return derivedFromResource;
515     }
516
517     private void setCapabilities(Map<String, Object> toscaJson, Resource resource, Resource parentResource) {// Note that parentResource can be null
518         Either<Map<String, Object>, ResultStatusEnum> toscaCapabilities = ImportUtils.findFirstToscaMapElement(toscaJson, TypeUtils.ToscaTagNamesEnum.CAPABILITIES);
519         if (toscaCapabilities.isLeft()) {
520             Map<String, Object> jsonCapabilities = toscaCapabilities.left().value();
521             Map<String, List<CapabilityDefinition>> moduleCapabilities = new HashMap<>();
522             Iterator<Entry<String, Object>> capabilitiesNameValue = jsonCapabilities.entrySet().iterator();
523             Set<String> capNames = new HashSet<>();
524             // Getting flattened list of capabilities of parent node - cap name
525             // to cap type
526             Map<String, String> capName2TypeMap = getCapName2Type(parentResource);
527             while (capabilitiesNameValue.hasNext()) {
528                 Entry<String, Object> capabilityNameValue = capabilitiesNameValue.next();
529
530                 // Validating that no req/cap duplicates exist in imported YAML
531                 String capNameLowerCase = capabilityNameValue.getKey().toLowerCase();
532                 if (capNames.contains(capNameLowerCase)) {
533                     log.debug("More than one capability with same name {} (case-insensitive) in imported TOSCA file is invalid", capNameLowerCase);
534                     throw new ByActionStatusComponentException(ActionStatus.IMPORT_DUPLICATE_REQ_CAP_NAME, "capability", capNameLowerCase);
535                 }
536                 capNames.add(capNameLowerCase);
537
538                 CapabilityDefinition capabilityDef = createCapabilityFromImportFile(capabilityNameValue
539                         .getValue());
540                 capabilityDef.setName(capabilityNameValue.getKey());
541                 if (moduleCapabilities.containsKey(capabilityDef.getType())) {
542                     moduleCapabilities.get(capabilityDef.getType()).add(capabilityDef);
543                 }
544                 else {
545                     List<CapabilityDefinition> list = new ArrayList<>();
546                     list.add(capabilityDef);
547                     moduleCapabilities.put(capabilityDef.getType(), list);
548                 }
549
550                 // Validating against req/cap of "derived from" node
551                 Boolean validateVsParentCap = validateCapNameVsDerived(capName2TypeMap, capabilityDef
552                         .getType(), capabilityDef.getName());
553
554                 if (!validateVsParentCap) {
555                     // Here parentResource is for sure not null, so it's
556                     // null-safe
557                     log.debug("Capability with name {} already exists in parent {}", capabilityDef.getName(), parentResource
558                             .getName());
559                     throw new ByActionStatusComponentException(ActionStatus.IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED, "capability", capabilityDef
560                             .getName()
561                             .toLowerCase(), parentResource.getName());
562                 }
563             }
564             if (moduleCapabilities.size() > 0) {
565                 resource.setCapabilities(moduleCapabilities);
566             }
567         }
568     }
569
570     private Map<String, String> getCapName2Type(Resource parentResource) {
571         Map<String, String> capName2type = new HashMap<>();
572         if (parentResource != null) {
573             Map<String, List<CapabilityDefinition>> capabilities = parentResource.getCapabilities();
574             if (capabilities != null) {
575                 for (List<CapabilityDefinition> capDefinitions : capabilities.values()) {
576                     for (CapabilityDefinition capDefinition : capDefinitions) {
577                         String nameLowerCase = capDefinition.getName().toLowerCase();
578                         if (capName2type.get(nameLowerCase) != null) {
579                             String parentResourceName = parentResource.getName();
580                             log.debug("Resource with name {} has more than one capability with name {}, ignoring case", parentResourceName, nameLowerCase);
581                             BeEcompErrorManager.getInstance()
582                                                .logInternalDataError("Import resource", "Parent resource " + parentResourceName + " of imported resource has one or more capabilities with name " + nameLowerCase, ErrorSeverity.ERROR);
583                             throw new ByActionStatusComponentException(ActionStatus.GENERAL_ERROR);
584                         }
585                         capName2type.put(nameLowerCase, capDefinition.getType());
586                     }
587                 }
588             }
589         }
590         return capName2type;
591     }
592
593     private Map<String, String> getReqName2Type(Resource parentResource) {
594         Map<String, String> reqName2type = new HashMap<>();
595         if (parentResource != null) {
596             Map<String, List<RequirementDefinition>> requirements = parentResource.getRequirements();
597             if (requirements != null) {
598                 for (List<RequirementDefinition> reqDefinitions : requirements.values()) {
599                     for (RequirementDefinition reqDefinition : reqDefinitions) {
600                         String nameLowerCase = reqDefinition.getName().toLowerCase();
601                         if (reqName2type.get(nameLowerCase) != null) {
602                             String parentResourceName = parentResource.getName();
603                             log.debug("Resource with name {} has more than one requirement with name {}, ignoring case", parentResourceName, nameLowerCase);
604                             BeEcompErrorManager.getInstance()
605                                                .logInternalDataError("Import resource", "Parent resource " + parentResourceName + " of imported resource has one or more requirements with name " + nameLowerCase, ErrorSeverity.ERROR);
606                             throw new ByActionStatusComponentException(ActionStatus.GENERAL_ERROR);
607                         }
608                         reqName2type.put(nameLowerCase, reqDefinition.getCapability());
609                     }
610                 }
611             }
612         }
613         return reqName2type;
614     }
615
616     private Boolean validateCapNameVsDerived(Map<String, String> parentCapName2Type, String childCapabilityType, String reqCapName) {
617         String capNameLowerCase = reqCapName.toLowerCase();
618         log.trace("Validating capability {} vs parent resource", capNameLowerCase);
619         String parentCapType = parentCapName2Type.get(capNameLowerCase);
620         if (parentCapType != null) {
621             if (childCapabilityType.equals(parentCapType)) {
622                 log.debug("Capability with name {} is of same type {} for imported resource and its parent - this is OK", capNameLowerCase, childCapabilityType);
623                 return true;
624             }
625             Either<Boolean, StorageOperationStatus> capabilityTypeDerivedFrom = capabilityTypeOperation.isCapabilityTypeDerivedFrom(childCapabilityType, parentCapType);
626             if (capabilityTypeDerivedFrom.isRight()) {
627                 log.debug("Couldn't check whether imported resource capability derives from its parent's capability");
628                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(capabilityTypeDerivedFrom
629                         .right()
630                         .value()));
631             }
632             return capabilityTypeDerivedFrom.left().value();
633         }
634         return true;
635     }
636
637     private CapabilityDefinition createCapabilityFromImportFile(Object capabilityJson) {
638
639         CapabilityDefinition capabilityDefinition = new CapabilityDefinition();
640
641         if (capabilityJson instanceof String) {
642             String capabilityJsonString = (String) capabilityJson;
643             capabilityDefinition.setType(capabilityJsonString);
644         }
645         else if (capabilityJson instanceof Map) {
646             Map<String, Object> capabilityJsonMap = (Map<String, Object>) capabilityJson;
647             // Type
648             if (capabilityJsonMap.containsKey(TypeUtils.ToscaTagNamesEnum.TYPE.getElementName())) {
649                 capabilityDefinition.setType((String) capabilityJsonMap.get(TypeUtils.ToscaTagNamesEnum.TYPE.getElementName()));
650             }
651             // ValidSourceTypes
652             if (capabilityJsonMap.containsKey(TypeUtils.ToscaTagNamesEnum.VALID_SOURCE_TYPES.getElementName())) {
653                 capabilityDefinition.setValidSourceTypes((List<String>) capabilityJsonMap.get(TypeUtils.ToscaTagNamesEnum.VALID_SOURCE_TYPES
654                         .getElementName()));
655             }
656             // ValidSourceTypes
657             if (capabilityJsonMap.containsKey(TypeUtils.ToscaTagNamesEnum.DESCRIPTION.getElementName())) {
658                 capabilityDefinition.setDescription((String) capabilityJsonMap.get(TypeUtils.ToscaTagNamesEnum.DESCRIPTION.getElementName()));
659             }
660             if (capabilityJsonMap.containsKey(TypeUtils.ToscaTagNamesEnum.OCCURRENCES.getElementName())) {
661                 List<Object> occurrencesList = (List) capabilityJsonMap.get(TypeUtils.ToscaTagNamesEnum.OCCURRENCES.getElementName());
662                 validateOccurrences(occurrencesList);
663                 capabilityDefinition.setMinOccurrences(occurrencesList.get(0).toString());
664                 capabilityDefinition.setMaxOccurrences(occurrencesList.get(1).toString());
665             }
666             if (capabilityJsonMap.containsKey(TypeUtils.ToscaTagNamesEnum.PROPERTIES.getElementName())) {
667
668                 Either<Map<String, PropertyDefinition>, ResultStatusEnum> propertiesRes = ImportUtils.getProperties(capabilityJsonMap);
669                 if (propertiesRes.isRight()) {
670                     throw new ByActionStatusComponentException(ActionStatus.PROPERTY_NOT_FOUND);
671                 }
672                 else {
673                     propertiesRes.left()
674                                  .value()
675                                  .entrySet()
676                                  .stream()
677                                  .forEach(e -> e.getValue().setName(e.getKey().toLowerCase()));
678                     List<ComponentInstanceProperty> capabilityProperties = propertiesRes.left()
679                                                                                         .value()
680                                                                                         .values()
681                                                                                         .stream()
682                                                                                         .map(p -> new ComponentInstanceProperty(p, p
683                                                                                                 .getDefaultValue(), null))
684                                                                                         .collect(Collectors.toList());
685                     capabilityDefinition.setProperties(capabilityProperties);
686                 }
687             }
688         }
689         else if (!(capabilityJson instanceof List)) {
690             throw new ByActionStatusComponentException(ActionStatus.INVALID_YAML);
691         }
692         return capabilityDefinition;
693     }
694
695     private void handleImportResourceException(UploadResourceInfo resourceMetaData, User user, boolean isNormative, RuntimeException e) {
696         ResponseFormat responseFormat;
697         ComponentException newException;
698         if (e instanceof ComponentException) {
699             ComponentException componentException = (ComponentException)e;
700             responseFormat = componentException.getResponseFormat();
701             if (responseFormat == null) {
702                 responseFormat = getResponseFormatManager().getResponseFormat(componentException.getActionStatus(), componentException.getParams());
703             }
704             newException = componentException;
705         }
706         else{
707             responseFormat = getResponseFormatManager().getResponseFormat(ActionStatus.GENERAL_ERROR);
708             newException = new ByActionStatusComponentException(ActionStatus.GENERAL_ERROR);
709         }
710         String payloadName = (resourceMetaData != null) ? resourceMetaData.getPayloadName() : "";
711         BeEcompErrorManager.getInstance().logBeSystemError("Import Resource " + payloadName);
712         log.debug("Error when importing resource from payload:{} Exception text: {}", payloadName, e.getMessage(), e);
713         auditErrorImport(resourceMetaData, user, responseFormat, isNormative);
714         throw newException;
715     }
716
717     private void auditErrorImport(UploadResourceInfo resourceMetaData, User user, ResponseFormat errorResponseWrapper, boolean isNormative) {
718         String version, lifeCycleState;
719         if (isNormative) {
720             version = TypeUtils.getFirstCertifiedVersionVersion();
721             lifeCycleState = LifecycleStateEnum.CERTIFIED.name();
722         }
723         else {
724             version = "";
725             lifeCycleState = LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name();
726
727         }
728
729         String message = "";
730         if (errorResponseWrapper.getMessageId() != null) {
731             message = errorResponseWrapper.getMessageId() + ": ";
732         }
733         message += errorResponseWrapper.getFormattedMessage();
734
735
736         AuditEventFactory factory = new AuditImportResourceAdminEventFactory(
737                 CommonAuditData.newBuilder()
738                                .status(errorResponseWrapper.getStatus())
739                                .description(message)
740                                .requestId(ThreadLocalsHolder.getUuid())
741                                .build(),
742                 new ResourceCommonInfo(resourceMetaData.getName(), ComponentTypeEnum.RESOURCE.getValue()),
743                 ResourceVersionInfo.newBuilder()
744                                  .state(lifeCycleState)
745                                  .version(version)
746                                  .build(),
747                 ResourceVersionInfo.newBuilder()
748                                  .state("")
749                                  .version("")
750                                  .build(),
751                 "", user, "");
752         getAuditingManager().auditEvent(factory);
753
754     }
755
756     private void setMetaDataFromJson(final UploadResourceInfo resourceMetaData, final Resource resource) {
757         this.populateResourceMetadata(resourceMetaData, resource);
758         resource.setCreatorUserId(resourceMetaData.getContactId());
759
760         final String payloadData = resourceMetaData.getPayloadData();
761         if (payloadData != null) {
762             resource.setToscaVersion(getToscaVersion(payloadData));
763         }
764
765         final List<CategoryDefinition> categories = resourceMetaData.getCategories();
766         calculateResourceIsAbstract(resource, categories);
767     }
768
769     private String getToscaVersion(final String payloadData) {
770         final String decodedPayload = new String(Base64.decodeBase64(payloadData));
771         final Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(decodedPayload);
772         final Either<String, ResultStatusEnum> findFirstToscaStringElement =
773             ImportUtils.findFirstToscaStringElement(mappedToscaTemplate, TypeUtils.ToscaTagNamesEnum.TOSCA_VERSION);
774         return findFirstToscaStringElement.left().value();
775     }
776
777     private void calculateResourceIsAbstract(Resource resource, List<CategoryDefinition> categories) {
778         if (categories != null && !categories.isEmpty()) {
779             CategoryDefinition categoryDef = categories.get(0);
780             resource.setAbstract(false);
781             if (categoryDef != null && categoryDef.getName() != null && categoryDef.getName()
782                                                                                    .equals(Constants.ABSTRACT_CATEGORY_NAME)) {
783                 SubCategoryDefinition subCategoryDef = categoryDef.getSubcategories().get(0);
784                 if (subCategoryDef != null && subCategoryDef.getName().equals(Constants.ABSTRACT_SUBCATEGORY)) {
785                     resource.setAbstract(true);
786                 }
787             }
788         }
789     }
790
791     private void setConstantMetaData(Resource resource, boolean shouldBeCertified) {
792         String version;
793         LifecycleStateEnum state;
794         if (shouldBeCertified) {
795             version = TypeUtils.getFirstCertifiedVersionVersion();
796             state = ImportUtils.Constants.NORMATIVE_TYPE_LIFE_CYCLE;
797         }
798         else {
799             version = ImportUtils.Constants.FIRST_NON_CERTIFIED_VERSION;
800             state = ImportUtils.Constants.NORMATIVE_TYPE_LIFE_CYCLE_NOT_CERTIFIED_CHECKOUT;
801         }
802         resource.setVersion(version);
803         resource.setLifecycleState(state);
804         resource.setHighestVersion(ImportUtils.Constants.NORMATIVE_TYPE_HIGHEST_VERSION);
805         resource.setVendorName(ImportUtils.Constants.VENDOR_NAME);
806         resource.setVendorRelease(ImportUtils.Constants.VENDOR_RELEASE);
807
808     }
809
810     private void validateOccurrences(List<Object> occurrensesList) {
811
812         if (!ValidationUtils.validateListNotEmpty(occurrensesList)) {
813             log.debug("Occurrenses list empty");
814             throw new ByActionStatusComponentException(ActionStatus.INVALID_OCCURRENCES);
815         }
816
817         if (occurrensesList.size() < 2) {
818             log.debug("Occurrenses list size not 2");
819             throw new ByActionStatusComponentException(ActionStatus.INVALID_OCCURRENCES);
820         }
821         Object minObj = occurrensesList.get(0);
822         Object maxObj = occurrensesList.get(1);
823         Integer minOccurrences;
824         Integer maxOccurrences;
825         if (minObj instanceof Integer) {
826             minOccurrences = (Integer) minObj;
827         }
828         else {
829             log.debug("Invalid occurrenses format. low_bound occurrense must be Integer {}", minObj);
830             throw new ByActionStatusComponentException(ActionStatus.INVALID_OCCURRENCES);
831         }
832         if (minOccurrences < 0) {
833             log.debug("Invalid occurrenses format.low_bound occurrense negative {}", minOccurrences);
834             throw new ByActionStatusComponentException(ActionStatus.INVALID_OCCURRENCES);
835         }
836
837         if (maxObj instanceof String){
838             if(!"UNBOUNDED".equals(maxObj)) {
839                 log.debug("Invalid occurrenses format. Max occurrence is {}", maxObj);
840                 throw new ByActionStatusComponentException(ActionStatus.INVALID_OCCURRENCES);
841             }
842         }
843         else {
844             if (maxObj instanceof Integer) {
845                 maxOccurrences = (Integer) maxObj;
846             }
847             else {
848                 log.debug("Invalid occurrenses format.  Max occurrence is {}", maxObj);
849                 throw new ByActionStatusComponentException(ActionStatus.INVALID_OCCURRENCES);
850             }
851
852             if (maxOccurrences <= 0 || maxOccurrences < minOccurrences) {
853                 log.debug("Invalid occurrenses format.  min occurrence is {}, Max occurrence is {}", minOccurrences, maxOccurrences);
854                 throw new ByActionStatusComponentException(ActionStatus.INVALID_OCCURRENCES);
855             }
856         }
857     }
858
859     public synchronized void init(ServletContext servletContext) {
860         if (this.servletContext == null) {
861             this.servletContext = servletContext;
862             responseFormatManager = ResponseFormatManager.getInstance();
863             resourceBusinessLogic = getResourceBL(servletContext);
864         }
865     }
866
867     public boolean isResourceExist(String resourceName) {
868         return resourceBusinessLogic.isResourceExist(resourceName);
869     }
870
871     private ResourceBusinessLogic getResourceBL(ServletContext context) {
872         WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(org.openecomp.sdc.common.api.Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
873         WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
874         return webApplicationContext.getBean(ResourceBusinessLogic.class);
875     }
876
877     public ServletContext getServletContext() {
878         return servletContext;
879     }
880
881     public AuditingManager getAuditingManager() {
882         return auditingManager;
883     }
884
885     public ResponseFormatManager getResponseFormatManager() {
886         return responseFormatManager;
887     }
888
889     public void setResponseFormatManager(ResponseFormatManager responseFormatManager) {
890         this.responseFormatManager = responseFormatManager;
891     }
892
893     public ResourceBusinessLogic getResourceBusinessLogic() {
894         return resourceBusinessLogic;
895     }
896
897     @Autowired
898     public void setResourceBusinessLogic(ResourceBusinessLogic resourceBusinessLogic) {
899         this.resourceBusinessLogic = resourceBusinessLogic;
900     }
901
902     public IGraphLockOperation getGraphLockOperation() {
903         return graphLockOperation;
904     }
905
906     @Autowired
907     public void setGraphLockOperation(IGraphLockOperation graphLockOperation) {
908         this.graphLockOperation = graphLockOperation;
909     }
910
911     public void setServletContext(ServletContext servletContext) {
912         this.servletContext = servletContext;
913     }
914
915     @Autowired
916     public void setAuditingManager(AuditingManager auditingManager) {
917         this.auditingManager = auditingManager;
918     }
919
920
921 }