Fix mismatch in requirement name in sub mapping
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / tosca / CapabilityRequirementConverter.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 package org.openecomp.sdc.be.tosca;
21
22 import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
23 import static org.apache.commons.lang3.StringUtils.isBlank;
24 import static org.apache.commons.lang3.StringUtils.isNoneBlank;
25
26 import com.google.common.collect.Iterables;
27 import com.google.common.collect.Maps;
28 import fj.data.Either;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Optional;
36 import java.util.function.Function;
37 import java.util.stream.Collectors;
38 import org.apache.commons.collections.CollectionUtils;
39 import org.apache.commons.collections.MapUtils;
40 import org.apache.commons.lang3.StringUtils;
41 import org.apache.commons.lang3.tuple.ImmutablePair;
42 import org.openecomp.sdc.be.datatypes.elements.CapabilityDataDefinition;
43 import org.openecomp.sdc.be.datatypes.elements.RequirementDataDefinition;
44 import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
45 import org.openecomp.sdc.be.model.CapabilityDefinition;
46 import org.openecomp.sdc.be.model.Component;
47 import org.openecomp.sdc.be.model.ComponentInstance;
48 import org.openecomp.sdc.be.model.ComponentInstanceProperty;
49 import org.openecomp.sdc.be.model.ComponentParametersView;
50 import org.openecomp.sdc.be.model.DataTypeDefinition;
51 import org.openecomp.sdc.be.model.GroupDefinition;
52 import org.openecomp.sdc.be.model.PropertyDefinition;
53 import org.openecomp.sdc.be.model.RequirementDefinition;
54 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade;
55 import org.openecomp.sdc.be.model.jsonjanusgraph.utils.ModelConverter;
56 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
57 import org.openecomp.sdc.be.model.utils.ComponentUtilities;
58 import org.openecomp.sdc.be.tosca.ToscaUtils.SubstitutionEntry;
59 import org.openecomp.sdc.be.tosca.model.SubstitutionMapping;
60 import org.openecomp.sdc.be.tosca.model.ToscaCapability;
61 import org.openecomp.sdc.be.tosca.model.ToscaNodeTemplate;
62 import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
63 import org.openecomp.sdc.be.tosca.model.ToscaProperty;
64 import org.openecomp.sdc.be.tosca.model.ToscaRequirement;
65 import org.openecomp.sdc.be.tosca.model.ToscaTemplateCapability;
66 import org.openecomp.sdc.common.log.wrappers.Logger;
67 import org.springframework.beans.factory.annotation.Autowired;
68 import org.springframework.context.annotation.Scope;
69
70 /**
71  * Allows to convert requirements\capabilities of a component to requirements\capabilities of a substitution mappings section of a tosca template
72  */
73 @org.springframework.stereotype.Component("capabilty-requirement-convertor")
74 @Scope(value = "singleton")
75 public class CapabilityRequirementConverter {
76
77     private static final String NO_CAPABILITIES = "No Capabilities for node type";
78     private static final String NO_REQUIREMENTS = "No Requirements for node type";
79     private static final Logger logger = Logger.getLogger(CapabilityRequirementConverter.class);
80     private static final String PATH_DELIMITER = ".";
81     private static final String FAILED_TO_FIND_CI_IN_PATH = "Failed to find ci in the path is {} component {}";
82     private static CapabilityRequirementConverter instance;
83     @Autowired
84     private ToscaOperationFacade toscaOperationFacade;
85     @Autowired
86     private PropertyConvertor propertyConvertor;
87
88     public CapabilityRequirementConverter() {
89     }
90
91     public static synchronized CapabilityRequirementConverter getInstance() {
92         if (instance == null) {
93             instance = new CapabilityRequirementConverter();
94         }
95         return instance;
96     }
97
98     public String buildCapabilityNameForComponentInstance(Map<String, Component> componentCache, ComponentInstance componentInstance,
99                                                           CapabilityDefinition c) {
100         String prefix = buildCapReqNamePrefix(componentInstance.getNormalizedName());
101         if (ComponentUtilities.isNotUpdatedCapReqName(prefix, c.getName(), c.getPreviousName())) {
102             return buildSubstitutedName(componentCache, c.getName(), c.getPreviousName(), c.getPath(), c.getOwnerId(), componentInstance).left()
103                 .orValue(c.getName());
104         }
105         return c.getPreviousName();
106     }
107
108     public String buildRequirementNameForComponentInstance(Map<String, Component> componentCache, ComponentInstance componentInstance,
109                                                            RequirementDefinition r) {
110         String prefix = buildCapReqNamePrefix(componentInstance.getNormalizedName());
111         if (ComponentUtilities.isNotUpdatedCapReqName(prefix, r.getName(), r.getPreviousName())) {
112             return buildSubstitutedName(componentCache, r.getName(), r.getPreviousName(), r.getPath(), r.getOwnerId(), componentInstance).left()
113                 .orValue(r.getName());
114         }
115         return r.getPreviousName();
116     }
117
118     private String buildCapReqNamePrefix(String normalizedName) {
119         return normalizedName + PATH_DELIMITER;
120     }
121
122     /**
123      * Allows to convert capabilities of a component to capabilities of a substitution mappings section of a tosca template
124      *
125      * @param componentInstance
126      * @param dataTypes
127      * @param nodeTemplate
128      * @return
129      */
130     public Either<ToscaNodeTemplate, ToscaError> convertComponentInstanceCapabilities(ComponentInstance componentInstance,
131                                                                                       Map<String, DataTypeDefinition> dataTypes,
132                                                                                       ToscaNodeTemplate nodeTemplate) {
133         Map<String, List<CapabilityDefinition>> capabilitiesInst = componentInstance.getCapabilities();
134         Map<String, Component> componentCache = new HashMap<>();
135         if (capabilitiesInst != null && !capabilitiesInst.isEmpty()) {
136             Map<String, ToscaTemplateCapability> capabilities = new HashMap<>();
137             capabilitiesInst.entrySet().forEach(e -> {
138                 List<CapabilityDefinition> capList = e.getValue();
139                 if (capList != null && !capList.isEmpty()) {
140                     capList.stream().forEach(c -> convertOverridenProperties(componentInstance, dataTypes, capabilities, c,
141                         buildCapabilityNameForComponentInstance(componentCache, componentInstance, c)));
142                 }
143             });
144             if (MapUtils.isNotEmpty(capabilities)) {
145                 nodeTemplate.setCapabilities(capabilities);
146             }
147         }
148         return Either.left(nodeTemplate);
149     }
150
151     private void convertOverridenProperties(ComponentInstance componentInstance, Map<String, DataTypeDefinition> dataTypes,
152                                             Map<String, ToscaTemplateCapability> capabilties, CapabilityDefinition c, String capabilityName) {
153         if (isNotEmpty(c.getProperties())) {
154             c.getProperties().stream().filter(p -> p.getValue() != null || p.getDefaultValue() != null)
155                 .forEach(p -> convertOverriddenProperty(componentInstance, dataTypes, capabilties, p, capabilityName));
156         }
157     }
158
159     private void convertOverriddenProperty(ComponentInstance componentInstance, Map<String, DataTypeDefinition> dataTypes,
160                                            Map<String, ToscaTemplateCapability> capabilties, ComponentInstanceProperty p, String capabilityName) {
161         if (logger.isDebugEnabled()) {
162             logger.debug("Exist d property {} for capability {} with value {}", p.getName(), capabilityName, p.getValue());
163         }
164         ToscaTemplateCapability toscaTemplateCapability = capabilties.computeIfAbsent(capabilityName, key -> new ToscaTemplateCapability());
165         Map<String, Object> toscaCapProp = toscaTemplateCapability.getProperties();
166         if (toscaCapProp == null) {
167             toscaCapProp = new HashMap<>();
168         }
169         Object convertedValue = convertInstanceProperty(dataTypes, componentInstance, p);
170         toscaCapProp.put(p.getName(), convertedValue);
171         toscaTemplateCapability.setProperties(toscaCapProp);
172     }
173
174     private Object convertInstanceProperty(Map<String, DataTypeDefinition> dataTypes, ComponentInstance componentInstance,
175                                            ComponentInstanceProperty prop) {
176         logger.debug("Convert property {} for instance {}", prop.getName(), componentInstance.getUniqueId());
177         String propValue = prop.getValue() == null ? prop.getDefaultValue() : prop.getValue();
178         return propertyConvertor.convertToToscaObject(prop, propValue, dataTypes, false);
179     }
180
181     /**
182      * Allows to convert requirements of a node type to tosca template requirements representation
183      *
184      * @param component
185      * @param nodeType
186      * @return
187      */
188     public Either<ToscaNodeType, ToscaError> convertRequirements(Map<String, Component> componentsCache, Component component,
189                                                                  ToscaNodeType nodeType) {
190         List<Map<String, ToscaRequirement>> toscaRequirements = convertRequirementsAsList(componentsCache, component);
191         if (!toscaRequirements.isEmpty()) {
192             nodeType.setRequirements(toscaRequirements);
193         }
194         logger.debug("Finish convert Requirements for node type");
195         return Either.left(nodeType);
196     }
197
198     /**
199      * Allows to convert component requirements to the tosca template substitution mappings requirements
200      *
201      * @param componentsCache
202      * @param component
203      * @param substitutionMappings
204      * @return
205      */
206     public Either<SubstitutionMapping, ToscaError> convertSubstitutionMappingRequirements(Map<String, Component> componentsCache, Component component,
207                                                                                           SubstitutionMapping substitutionMappings) {
208         Either<SubstitutionMapping, ToscaError> result = Either.left(substitutionMappings);
209         Either<Map<String, String[]>, ToscaError> toscaRequirementsRes = convertSubstitutionMappingRequirementsAsMap(componentsCache, component);
210         if (toscaRequirementsRes.isRight()) {
211             result = Either.right(toscaRequirementsRes.right().value());
212             logger.debug("Failed convert requirements for the component {}. ", component.getName());
213         } else if (MapUtils.isNotEmpty(toscaRequirementsRes.left().value())) {
214             substitutionMappings.setRequirements(toscaRequirementsRes.left().value());
215             result = Either.left(substitutionMappings);
216             logger.debug("Finish convert requirements for the component {}. ", component.getName());
217         }
218         return result;
219     }
220
221     /**
222      * Allows to convert requirements of a server proxy node type to tosca template requirements
223      *
224      * @param instanceProxy
225      * @return converted tosca template requirements
226      */
227     List<Map<String, ToscaRequirement>> convertProxyRequirements(Map<String, Component> componentCache, ComponentInstance instanceProxy) {
228         Map<String, List<RequirementDefinition>> requirements = instanceProxy.getRequirements();
229         List<Map<String, ToscaRequirement>> toscaRequirements = new ArrayList<>();
230         if (requirements != null) {
231             requirements.entrySet().stream().flatMap(e -> e.getValue().stream()).forEach(req -> {
232                 ImmutablePair<String, ToscaRequirement> pair = convertProxyRequirement(
233                     buildRequirementNameForComponentInstance(componentCache, instanceProxy, req), req);
234                 Map<String, ToscaRequirement> requirement = new HashMap<>();
235                 requirement.put(pair.left, pair.right);
236                 toscaRequirements.add(requirement);
237             });
238         } else {
239             logger.debug(NO_REQUIREMENTS);
240         }
241         return toscaRequirements;
242     }
243
244     private ImmutablePair<String, ToscaRequirement> convertProxyRequirement(String requirementName, RequirementDefinition r) {
245         ToscaRequirement toscaRequirement = createToscaRequirement(r);
246         return new ImmutablePair<>(requirementName, toscaRequirement);
247     }
248
249     private List<Map<String, ToscaRequirement>> convertRequirementsAsList(Map<String, Component> componentsCache, Component component) {
250         Map<String, List<RequirementDefinition>> requirements = component.getRequirements();
251         List<Map<String, ToscaRequirement>> toscaRequirements = new ArrayList<>();
252         if (requirements != null) {
253             for (Map.Entry<String, List<RequirementDefinition>> entry : requirements.entrySet()) {
254                 entry.getValue().stream().filter(r -> filter(component, r.getOwnerId())).forEach(r -> {
255                     ImmutablePair<String, ToscaRequirement> pair = convertRequirement(componentsCache, component,
256                         ModelConverter.isAtomicComponent(component), r);
257                     Map<String, ToscaRequirement> requirement = new HashMap<>();
258                     requirement.put(pair.left, pair.right);
259                     toscaRequirements.add(requirement);
260                 });
261                 logger.debug("Finish convert Requirements for node type");
262             }
263         } else {
264             logger.debug(NO_REQUIREMENTS);
265         }
266         return toscaRequirements;
267     }
268
269     private boolean filter(Component component, String ownerId) {
270         return !ModelConverter.isAtomicComponent(component) || isNodeTypeOwner(component, ownerId) || (ModelConverter.isAtomicComponent(component)
271             && ownerId == null);
272     }
273
274     private boolean isNodeTypeOwner(Component component, String ownerId) {
275         return ModelConverter.isAtomicComponent(component) && component.getUniqueId().equals(ownerId);
276     }
277
278     private String dropLast(String path, String delimiter) {
279         if (isBlank(path) || isBlank(delimiter)) {
280             return path;
281         }
282         return path.substring(0, path.lastIndexOf(delimiter));
283     }
284
285     private Either<Map<String, String[]>, ToscaError> convertSubstitutionMappingRequirementsAsMap(Map<String, Component> componentsCache,
286                                                                                                   Component component) {
287         Map<String, List<RequirementDefinition>> requirements = component.getRequirements();
288         Either<Map<String, String[]>, ToscaError> result;
289         if (requirements != null) {
290             result = buildAddSubstitutionMappingsRequirements(componentsCache, component, requirements);
291         } else {
292             result = Either.left(Maps.newHashMap());
293             logger.debug("No requirements for substitution mappings section of a tosca template of the component {}. ", component.getName());
294         }
295         return result;
296     }
297
298     private Either<Map<String, String[]>, ToscaError> buildAddSubstitutionMappingsRequirements(Map<String, Component> componentsCache,
299                                                                                                Component component,
300                                                                                                Map<String, List<RequirementDefinition>> requirements) {
301         Map<String, String[]> toscaRequirements = new HashMap<>();
302         Either<Map<String, String[]>, ToscaError> result = null;
303         for (Map.Entry<String, List<RequirementDefinition>> entry : requirements.entrySet()) {
304             Optional<RequirementDefinition> failedToAddRequirement = addExternalToToscaRequirements(componentsCache, toscaRequirements, component, entry.getValue());
305             if (failedToAddRequirement.isPresent()) {
306                 logger.debug("Failed to convert requirement {} for substitution mappings section of a tosca template of the component {}. ",
307                     failedToAddRequirement.get().getName(), component.getName());
308                 result = Either.right(ToscaError.NODE_TYPE_REQUIREMENT_ERROR);
309             }
310             logger.debug("Finish convert requirements for the component {}. ", component.getName());
311         }
312         if (result == null) {
313             result = Either.left(toscaRequirements);
314         }
315         return result;
316     }
317     
318     private Optional<RequirementDefinition> addExternalToToscaRequirements(final Map<String, Component> componentsCache, 
319             final Map<String, String[]> toscaRequirements, final Component component, final List<RequirementDefinition> requirements) {
320         return requirements.stream().filter(RequirementDefinition::isExternal).filter(
321                 r -> !
322                 (StringUtils.isEmpty(r.getExternalName()) ?
323                     addEntry(componentsCache, toscaRequirements, component, new SubstitutionEntry(r.getName(), r.getParentName(), ""),
324                         r.getPreviousName(), r.getOwnerId(), r.getPath()): 
325                     addEntry(componentsCache, toscaRequirements, component, new SubstitutionEntry(r.getExternalName(), r.getPreviousName() == null ? r.getName(): r.getPreviousName(), ""),
326                         r.getPreviousName(), r.getOwnerId(), r.getPath(), false))
327                     ).findAny();
328     }
329
330     private Either<Map<String, String[]>, ToscaError> buildAddSubstitutionMappingsCapabilities(Map<String, Component> componentsCache,
331                                                                                                Component component,
332                                                                                                Map<String, List<CapabilityDefinition>> capabilities) {
333         Map<String, String[]> toscaCapabilities = new HashMap<>();
334         Either<Map<String, String[]>, ToscaError> result = null;
335         for (Map.Entry<String, List<CapabilityDefinition>> entry : capabilities.entrySet()) {
336             Optional<CapabilityDefinition> failedToAddRequirement = addExternalToToscaCapabilities(componentsCache, toscaCapabilities, component, entry.getValue());
337             if (failedToAddRequirement.isPresent()) {
338                 logger.debug("Failed to convert capability {} for substitution mappings section of a tosca template of the component {}. ",
339                     failedToAddRequirement.get().getName(), component.getName());
340                 result = Either.right(ToscaError.NODE_TYPE_CAPABILITY_ERROR);
341             }
342             logger.debug("Finish convert capabilities for the component {}. ", component.getName());
343         }
344         if (result == null) {
345             result = Either.left(toscaCapabilities);
346         }
347         return result;
348     }
349     
350     private Optional<CapabilityDefinition> addExternalToToscaCapabilities(final Map<String, Component> componentsCache, 
351             final Map<String, String[]> toscaCapabilities, final Component component, final List<CapabilityDefinition> requirements) {
352         return requirements.stream()
353                 .filter(CapabilityDataDefinition::isExternal)
354                 .filter( c -> !
355                         (StringUtils.isEmpty(c.getExternalName()) ?
356                                 addEntry(componentsCache, toscaCapabilities, component, new SubstitutionEntry(c.getName(), c.getParentName(), ""),
357                                     c.getPreviousName(), c.getOwnerId(), c.getPath()):
358                                 addEntry(componentsCache, toscaCapabilities, component, new SubstitutionEntry(c.getName(), c.getParentName(), ""),
359                                     c.getPreviousName(), c.getOwnerId(), c.getPath(), false))
360                 ).findAny();
361     }
362
363     private boolean addEntry(Map<String, Component> componentsCache, Map<String, String[]> capReqMap, Component component, SubstitutionEntry entry,
364                              String previousName, String ownerId, List<String> path) {
365         return addEntry(componentsCache, capReqMap, component, entry, previousName, ownerId, path, shouldBuildSubstitutionName(component, path));
366     }
367     
368     private boolean addEntry(final Map<String, Component> componentsCache, Map<String, String[]> capReqMap, final Component component, 
369             final SubstitutionEntry entry, final String previousName, final String ownerId, final List<String> path, final boolean shouldBuildSubstitutionName) {
370         if (shouldBuildSubstitutionName && !buildSubstitutedNamePerInstance(componentsCache,
371                 component, entry.getFullName(), previousName, path, ownerId, entry)) {
372             return false;
373         }
374         logger.debug("The requirement/capability {} belongs to the component {} ", entry.getFullName(),
375                 component.getUniqueId());
376         if (StringUtils.isNotEmpty(entry.getSourceName())) {
377             addEntry(capReqMap, component, path, entry);
378         }
379         logger.debug("Finish convert the requirement/capability {} for the component {}. ", entry.getFullName(),
380                 component.getName());
381         return true;
382     }
383
384     private boolean shouldBuildSubstitutionName(Component component, List<String> path) {
385         return ToscaUtils.isNotComplexVfc(component) && isNotEmpty(path) && path.iterator().hasNext();
386     }
387
388     private boolean buildSubstitutedNamePerInstance(Map<String, Component> componentsCache, Component component, String name, String previousName,
389                                                     List<String> path, String ownerId, SubstitutionEntry entry) {
390         String fullName;
391         String sourceName;
392         String prefix;
393         if (CollectionUtils.isNotEmpty(component.getGroups())) {
394             Optional<GroupDefinition> groupOpt = component.getGroups().stream().filter(g -> g.getUniqueId().equals(ownerId)).findFirst();
395             if (groupOpt.isPresent()) {
396                 prefix = buildCapReqNamePrefix(groupOpt.get().getNormalizedName());
397                 if (ComponentUtilities.isNotUpdatedCapReqName(prefix, name, previousName)) {
398                     sourceName = name;
399                     fullName = prefix + sourceName;
400                 } else {
401                     sourceName = previousName;
402                     fullName = name;
403                 }
404                 entry.setFullName(fullName);
405                 entry.setSourceName(sourceName);
406                 entry.setOwner(groupOpt.get().getNormalizedName());
407                 return true;
408             }
409         }
410         Optional<ComponentInstance> ci = component.safeGetComponentInstances().stream().filter(c -> c.getUniqueId().equals(Iterables.getLast(path)))
411             .findFirst();
412         if (!ci.isPresent()) {
413             logger.debug(FAILED_TO_FIND_CI_IN_PATH, path, component.getUniqueId());
414             Collections.reverse(path);
415             logger.debug("try to reverse path {} component {}", path, component.getUniqueId());
416             ci = component.safeGetComponentInstances().stream().filter(c -> c.getUniqueId().equals(Iterables.getLast(path))).findFirst();
417         }
418         if (ci.isPresent()) {
419             prefix = buildCapReqNamePrefix(ci.get().getNormalizedName());
420             if (ComponentUtilities.isNotUpdatedCapReqName(prefix, name, previousName)) {
421                 Either<String, Boolean> buildSubstitutedName = buildSubstitutedName(componentsCache, name, previousName, path, ownerId, ci.get());
422                 if (buildSubstitutedName.isRight()) {
423                     logger.debug("Failed buildSubstitutedName name {}  path {} component {}", name, path, component.getUniqueId());
424                     return false;
425                 }
426                 sourceName = buildSubstitutedName.left().value();
427                 fullName = prefix + sourceName;
428             } else {
429                 sourceName = previousName;
430                 fullName = name;
431             }
432             entry.setFullName(fullName);
433             entry.setSourceName(sourceName);
434         } else {
435             logger.debug(FAILED_TO_FIND_CI_IN_PATH, path, component.getUniqueId());
436             return false;
437         }
438         return true;
439     }
440
441     private void addEntry(Map<String, String[]> toscaRequirements, Component component, List<String> capPath, SubstitutionEntry entry) {
442         Optional<ComponentInstance> findFirst = component.safeGetComponentInstances().stream()
443             .filter(ci -> ci.getUniqueId().equals(Iterables.getLast(capPath))).findFirst();
444         findFirst.ifPresent(componentInstance -> entry.setOwner(componentInstance.getName()));
445         if (StringUtils.isNotEmpty(entry.getOwner()) && StringUtils.isNotEmpty(entry.getSourceName())) {
446             toscaRequirements.put(entry.getFullName(), new String[]{entry.getOwner(), entry.getSourceName()});
447         }
448     }
449
450     public Either<String, Boolean> buildSubstitutedName(Map<String, Component> componentsCache, String name, String previousName, List<String> path,
451                                                         String ownerId, ComponentInstance instance) {
452         if (StringUtils.isNotEmpty(previousName)) {
453             return Either.left(name);
454         }
455         Either<Component, Boolean> getOriginRes = getOriginComponent(componentsCache, instance);
456         if (getOriginRes.isRight()) {
457             logger
458                 .debug("Failed to build substituted name for the capability/requirement {}. Failed to get an origin component with uniqueId {}", name,
459                     instance.getComponentUid());
460             return Either.right(false);
461         }
462         List<String> reducedPath = ownerId != null ? getReducedPathByOwner(path, ownerId) : getReducedPath(path);
463         logger.debug("reducedPath for ownerId {}, reducedPath {} ", ownerId, reducedPath);
464         reducedPath.remove(reducedPath.size() - 1);
465         return buildSubstitutedName(componentsCache, getOriginRes.left().value(), reducedPath, name, previousName);
466     }
467
468     private String buildReqNamePerOwnerByPath(Map<String, Component> componentsCache, Component component, RequirementDefinition r) {
469         return buildCapReqNamePerOwnerByPath(componentsCache, component, r.getName(), r.getPreviousName(), r.getPath());
470     }
471
472     private ImmutablePair<String, ToscaRequirement> convertRequirement(Map<String, Component> componentsCache, Component component,
473                                                                        boolean isNodeType, RequirementDefinition r) {
474         String name;
475         if (StringUtils.isEmpty(r.getExternalName())){
476             name = r.getName();
477             if (!isNodeType && ToscaUtils.isNotComplexVfc(component)) {
478                 name = buildReqNamePerOwnerByPath(componentsCache, component, r);
479             }
480         } else {
481             name = r.getExternalName();
482         }
483         
484         logger.debug("the requirement {} belongs to resource {} ", name, component.getUniqueId());
485         ToscaRequirement toscaRequirement = createToscaRequirement(r);
486         return new ImmutablePair<>(name, toscaRequirement);
487     }
488
489     private ToscaRequirement createToscaRequirement(RequirementDefinition r) {
490         ToscaRequirement toscaRequirement = new ToscaRequirement();
491         List<Object> occurrences = new ArrayList<>();
492         occurrences.add(Integer.valueOf(r.getMinOccurrences()));
493         if (r.getMaxOccurrences().equals(RequirementDataDefinition.MAX_OCCURRENCES)) {
494             occurrences.add(r.getMaxOccurrences());
495         } else {
496             occurrences.add(Integer.valueOf(r.getMaxOccurrences()));
497         }
498         toscaRequirement.setOccurrences(occurrences);
499         toscaRequirement.setNode(r.getNode());
500         toscaRequirement.setCapability(r.getCapability());
501         toscaRequirement.setRelationship(r.getRelationship());
502         return toscaRequirement;
503     }
504
505     /**
506      * Allows to convert capabilities of a node type to tosca template capabilities
507      *
508      * @param component
509      * @param dataTypes
510      * @return
511      */
512     public Map<String, ToscaCapability> convertCapabilities(Map<String, Component> componentsCache, Component component,
513                                                             Map<String, DataTypeDefinition> dataTypes) {
514         Map<String, List<CapabilityDefinition>> capabilities = component.getCapabilities();
515         Map<String, ToscaCapability> toscaCapabilities = new HashMap<>();
516         if (capabilities != null) {
517             boolean isNodeType = ModelConverter.isAtomicComponent(component);
518             for (Map.Entry<String, List<CapabilityDefinition>> entry : capabilities.entrySet()) {
519                 entry.getValue().stream().filter(c -> filter(component, c.getOwnerId()))
520                     .forEach(c -> convertCapability(componentsCache, component, toscaCapabilities, isNodeType, c, dataTypes, c.getName()));
521             }
522         } else {
523             logger.debug(NO_CAPABILITIES);
524         }
525         return toscaCapabilities;
526     }
527
528     /**
529      * Allows to convert capabilities of a server proxy node type to tosca template capabilities
530      *
531      * @param instanceProxy
532      * @param dataTypes
533      * @return
534      */
535     public Map<String, ToscaCapability> convertProxyCapabilities(Map<String, Component> componentCache, ComponentInstance instanceProxy,
536                                                                  Map<String, DataTypeDefinition> dataTypes) {
537         Map<String, List<CapabilityDefinition>> capabilities = instanceProxy.getCapabilities();
538         Map<String, ToscaCapability> toscaCapabilities = new HashMap<>();
539         if (capabilities != null) {
540             for (Map.Entry<String, List<CapabilityDefinition>> entry : capabilities.entrySet()) {
541                 entry.getValue().stream().forEach(c -> convertProxyCapability(toscaCapabilities, c, dataTypes,
542                     buildCapabilityNameForComponentInstance(componentCache, instanceProxy, c)));
543             }
544         } else {
545             logger.debug(NO_CAPABILITIES);
546         }
547         return toscaCapabilities;
548     }
549
550     /**
551      * Allows to convert component capabilities to the tosca template substitution mappings capabilities
552      *
553      * @param componentsCache
554      * @param component
555      * @return
556      */
557     public Either<Map<String, String[]>, ToscaError> convertSubstitutionMappingCapabilities(Map<String, Component> componentsCache,
558                                                                                             Component component) {
559         Map<String, List<CapabilityDefinition>> capabilities = component.getCapabilities();
560         Either<Map<String, String[]>, ToscaError> res;
561         if (capabilities != null) {
562             res = buildAddSubstitutionMappingsCapabilities(componentsCache, component, capabilities);
563         } else {
564             res = Either.left(Maps.newHashMap());
565             logger.debug(NO_CAPABILITIES);
566         }
567         return res;
568     }
569
570     private String buildCapNamePerOwnerByPath(Map<String, Component> componentsCache, CapabilityDefinition c, Component component) {
571         return buildCapReqNamePerOwnerByPath(componentsCache, component, c.getName(), c.getPreviousName(), c.getPath());
572     }
573
574     private void convertProxyCapability(Map<String, ToscaCapability> toscaCapabilities, CapabilityDefinition c,
575                                         Map<String, DataTypeDefinition> dataTypes, String capabilityName) {
576         createToscaCapability(toscaCapabilities, c, dataTypes, capabilityName);
577     }
578
579     private void convertCapability(Map<String, Component> componentsCache, Component component, Map<String, ToscaCapability> toscaCapabilities,
580                                    boolean isNodeType, CapabilityDefinition c, Map<String, DataTypeDefinition> dataTypes, String capabilityName) {
581         String name;
582         if (StringUtils.isEmpty(c.getExternalName())) {
583             name = isNoneBlank(capabilityName) ? capabilityName : c.getName();
584             if (!isNodeType && ToscaUtils.isNotComplexVfc(component)) {
585                 name = buildCapNamePerOwnerByPath(componentsCache, c, component);
586             }
587         } else {
588             name = c.getExternalName();
589         }
590         logger.debug("The capability {} belongs to resource {} ", name, component.getUniqueId());
591         createToscaCapability(toscaCapabilities, c, dataTypes, name);
592     }
593
594     private void createToscaCapability(Map<String, ToscaCapability> toscaCapabilities, CapabilityDefinition c,
595                                        Map<String, DataTypeDefinition> dataTypes, String name) {
596         ToscaCapability toscaCapability = new ToscaCapability();
597         toscaCapability.setDescription(c.getDescription());
598         toscaCapability.setType(c.getType());
599         List<Object> occurrences = new ArrayList<>();
600         occurrences.add(Integer.valueOf(c.getMinOccurrences()));
601         if (c.getMaxOccurrences().equals(CapabilityDataDefinition.MAX_OCCURRENCES)) {
602             occurrences.add(c.getMaxOccurrences());
603         } else {
604             occurrences.add(Integer.valueOf(c.getMaxOccurrences()));
605         }
606         toscaCapability.setOccurrences(occurrences);
607         toscaCapability.setValid_source_types(c.getValidSourceTypes());
608         List<ComponentInstanceProperty> properties = c.getProperties();
609         if (isNotEmpty(properties)) {
610             Map<String, ToscaProperty> toscaProperties = new HashMap<>();
611             for (PropertyDefinition property : properties) {
612                 ToscaProperty toscaProperty = propertyConvertor.convertProperty(dataTypes, property, PropertyConvertor.PropertyType.CAPABILITY);
613                 toscaProperties.put(property.getName(), toscaProperty);
614             }
615             toscaCapability.setProperties(toscaProperties);
616         }
617         toscaCapabilities.put(name, toscaCapability);
618     }
619
620     private String buildCapReqNamePerOwnerByPath(Map<String, Component> componentsCache, Component component, String name, String previousName,
621                                                  List<String> path) {
622         if (CollectionUtils.isEmpty(path)) {
623             return name;
624         }
625         String ownerId = path.get(path.size() - 1);
626         String prefix;
627         if (CollectionUtils.isNotEmpty(component.getGroups())) {
628             Optional<GroupDefinition> groupOpt = component.getGroups().stream().filter(g -> g.getUniqueId().equals(ownerId)).findFirst();
629             if (groupOpt.isPresent()) {
630                 prefix = buildCapReqNamePrefix(groupOpt.get().getNormalizedName());
631                 if (ComponentUtilities.isNotUpdatedCapReqName(prefix, name, previousName)) {
632                     return prefix + name;
633                 }
634                 return name;
635             }
636         }
637         Optional<ComponentInstance> ci = component.safeGetComponentInstances().stream().filter(c -> c.getUniqueId().equals(Iterables.getLast(path)))
638             .findFirst();
639         if (!ci.isPresent()) {
640             logger.debug(FAILED_TO_FIND_CI_IN_PATH, path, component.getUniqueId());
641             Collections.reverse(path);
642             logger.debug("try to reverse path {} component {}", path, component.getUniqueId());
643             ci = component.safeGetComponentInstances().stream().filter(c -> c.getUniqueId().equals(Iterables.getLast(path))).findFirst();
644         }
645         if (ci.isPresent()) {
646             prefix = buildCapReqNamePrefix(ci.get().getNormalizedName());
647             if (ComponentUtilities.isNotUpdatedCapReqName(prefix, name, previousName)) {
648                 Either<String, Boolean> buildSubstitutedName = buildSubstitutedName(componentsCache, name, previousName, path, ownerId, ci.get());
649                 if (buildSubstitutedName.isRight()) {
650                     logger.debug("Failed buildSubstitutedName name {}  path {} component {}", name, path, component.getUniqueId());
651                 }
652                 return prefix + buildSubstitutedName.left().value();
653             }
654             return name;
655         }
656         return StringUtils.EMPTY;
657     }
658
659     /**
660      * Allows to build substituted name of capability\requirement of the origin component instance according to the path
661      *
662      * @param componentsCache
663      * @param originComponent
664      * @param path
665      * @param name
666      * @param previousName
667      * @return
668      */
669     public Either<String, Boolean> buildSubstitutedName(Map<String, Component> componentsCache, Component originComponent, List<String> path,
670                     String name, String previousName) {
671         return buildSubstitutedName(componentsCache, originComponent, path, name, previousName, null);
672     }
673     
674     public Either<String, Boolean> buildSubstitutedName(Map<String, Component> componentsCache, Component originComponent, List<String> path,
675                                                         String name, String previousName, String externalName) {
676         if (StringUtils.isNotEmpty(externalName)) {
677             return Either.left(externalName);
678         }
679         if (StringUtils.isNotEmpty(previousName)) {
680             return Either.left(name);
681         }
682         StringBuilder substitutedName = new StringBuilder();
683         boolean nameBuiltSuccessfully = true;
684         if (isNotEmpty(path) && ToscaUtils.isNotComplexVfc(originComponent)) {
685             List<String> reducedPath = getReducedPath(path);
686             Collections.reverse(reducedPath);
687             nameBuiltSuccessfully = appendNameRecursively(componentsCache, originComponent, reducedPath.iterator(), substitutedName);
688         }
689         return nameBuiltSuccessfully ? Either.left(substitutedName.append(name).toString()) : Either.right(nameBuiltSuccessfully);
690     }
691
692     protected List<String> getReducedPathByOwner(List<String> path, String ownerId) {
693         logger.debug("ownerId {}, path {} ", ownerId, path);
694         if (CollectionUtils.isEmpty(path)) {
695             logger.debug("cannot perform reduce by owner, path to component is empty");
696             return path;
697         }
698         if (isBlank(ownerId)) {
699             logger.debug("cannot perform reduce by owner, component owner is empty");
700             return path;
701         }
702         //reduce by owner
703         Map map = path.stream()
704             .collect(Collectors.toMap(it -> dropLast(it, PATH_DELIMITER), Function.identity(), (a, b) -> a.endsWith(ownerId) ? a : b));
705         //reduce list&duplicates and preserve order
706         return path.stream().distinct().filter(it -> map.values().contains(it)).collect(Collectors.toList());
707     }
708
709     private List<String> getReducedPath(List<String> path) {
710         return path.stream().distinct().collect(Collectors.toList());
711     }
712
713     private boolean appendNameRecursively(Map<String, Component> componentsCache, Component originComponent, Iterator<String> instanceIdIter,
714                                           StringBuilder substitutedName) {
715         if (isNotEmpty(originComponent.getComponentInstances()) && instanceIdIter.hasNext() && ToscaUtils.isNotComplexVfc(originComponent)) {
716             String ownerId = instanceIdIter.next();
717             Optional<ComponentInstance> instanceOpt = originComponent.getComponentInstances().stream().filter(i -> i.getUniqueId().equals(ownerId))
718                 .findFirst();
719             if (instanceOpt.isPresent()) {
720                 substitutedName.append(instanceOpt.get().getNormalizedName()).append(PATH_DELIMITER);
721                 Either<Component, Boolean> getOriginRes = getOriginComponent(componentsCache, instanceOpt.get());
722                 if (getOriginRes.isRight()) {
723                     return false;
724                 }
725                 appendNameRecursively(componentsCache, getOriginRes.left().value(), instanceIdIter, substitutedName);
726             } else if (CollectionUtils.isNotEmpty(originComponent.getGroups())) {
727                 Optional<GroupDefinition> groupOpt = originComponent.getGroups().stream().filter(g -> g.getUniqueId().equals(ownerId)).findFirst();
728                 if (!groupOpt.isPresent()) {
729                     logger.debug("Failed to find an capability owner with uniqueId {} on a component with uniqueId {}", ownerId,
730                         originComponent.getUniqueId());
731                     return false;
732                 }
733                 substitutedName.append(groupOpt.get().getNormalizedName()).append(PATH_DELIMITER);
734             } else {
735                 logger.debug("Failed to find an capability owner with uniqueId {} on a component with uniqueId {}", ownerId,
736                     originComponent.getUniqueId());
737                 return false;
738             }
739         }
740         return true;
741     }
742
743     Either<Component, Boolean> getOriginComponent(Map<String, Component> componentsCache, ComponentInstance instance) {
744         Either<Component, Boolean> result;
745         Either<Component, StorageOperationStatus> getOriginRes;
746         if (componentsCache.containsKey(instance.getActualComponentUid())) {
747             result = Either.left(componentsCache.get(instance.getActualComponentUid()));
748         } else {
749             ComponentParametersView filter = getFilter(instance);
750             getOriginRes = toscaOperationFacade.getToscaElement(instance.getActualComponentUid(), filter);
751             if (getOriginRes.isRight()) {
752                 logger.debug("Failed to get an origin component with uniqueId {}", instance.getActualComponentUid());
753                 result = Either.right(false);
754             } else {
755                 result = Either.left(getOriginRes.left().value());
756                 componentsCache.put(getOriginRes.left().value().getUniqueId(), getOriginRes.left().value());
757             }
758         }
759         return result;
760     }
761
762     private ComponentParametersView getFilter(ComponentInstance instance) {
763         ComponentParametersView filter = new ComponentParametersView(true);
764         filter.setIgnoreComponentInstances(false);
765         if (instance.getIsProxy()) {
766             filter.setIgnoreCapabilities(false);
767             filter.setIgnoreRequirements(false);
768             filter.setIgnoreCategories(false);
769         }
770         if (instance.getOriginType() == OriginTypeEnum.VF) {
771             filter.setIgnoreGroups(false);
772         }
773         return filter;
774     }
775 }