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