package org.alien4cloud.tosca.topology; import java.util.*; import java.util.stream.Collectors; import org.alien4cloud.tosca.editor.EditionContext; import org.alien4cloud.tosca.model.CSARDependency; import org.alien4cloud.tosca.model.definitions.CapabilityDefinition; import org.alien4cloud.tosca.model.definitions.PropertyDefinition; import org.alien4cloud.tosca.model.definitions.RequirementDefinition; import org.alien4cloud.tosca.model.templates.AbstractTemplate; import org.alien4cloud.tosca.model.templates.NodeTemplate; import org.alien4cloud.tosca.model.templates.Topology; import org.alien4cloud.tosca.model.types.*; import org.springframework.stereotype.Service; import com.google.common.collect.Maps; import alien4cloud.topology.AbstractTopologyDTO; import alien4cloud.topology.DependencyConflictDTO; import alien4cloud.topology.TopologyDTO; import alien4cloud.tosca.context.ToscaContext; import alien4cloud.tosca.context.ToscaContextual; import alien4cloud.tosca.normative.ToscaType; /** * Service that helps to create a topology dto object out of a topology. */ @Service public class TopologyDTOBuilder { /** * Build a topology dto (topology and all used types) out of a topology. * * @param context The edition context from which to build the dto. */ @ToscaContextual public TopologyDTO buildTopologyDTO(EditionContext context) { TopologyDTO topologyDTO = new TopologyDTO(); buildAbstractTopologyDTO(context.getTopology(), topologyDTO); topologyDTO.setArchiveContentTree(context.getArchiveContentTree()); topologyDTO.setLastOperationIndex(context.getLastOperationIndex()); topologyDTO.setOperations(context.getOperations()); topologyDTO.setDelegateType(context.getCsar().getDelegateType()); topologyDTO.setDependencyConflicts(getDependencyConflictDTOs(context)); // FIXME add validation information return topologyDTO; } /** * Compute a list of transitive dependency conflicts from the Context. * @param context the EditionContext of the Topology being built. * @return a list of dependency conflicts. */ private List<DependencyConflictDTO> getDependencyConflictDTOs(EditionContext context) { // Generate a map with all transitive dependency conflict for each dependency in the context. final Set<CSARDependency> dependencies = context.getToscaContext().getDependencies(); Map<CSARDependency, Set<CSARDependency>> dependencyConflictMap = new HashMap<>(); dependencies.forEach(source -> { final Set<CSARDependency> transitives = Optional.ofNullable(ToscaContext.get().getArchive(source.getName(), source.getVersion()).getDependencies()) .orElse(Collections.emptySet()) .stream().filter((o) -> !dependencies.contains(o)).collect(Collectors.toSet()); if (!transitives.isEmpty()) dependencyConflictMap.put(source, transitives); }); final ArrayList<DependencyConflictDTO> dependencyConflicts = new ArrayList<>(); dependencyConflictMap.forEach((source, conflicts) -> conflicts.forEach(conflict -> { String actualVersion = dependencies.stream().filter(d -> d.getName().equals(conflict.getName())).findFirst().map(CSARDependency::getVersion).orElse(""); dependencyConflicts.add(new DependencyConflictDTO(source.getName(), conflict.getName() + ":" + conflict.getVersion(), actualVersion)); }) ); return dependencyConflicts; } /** * Build a topology dto from a topology. * * @param topology The topology from which to build the DTO object. * @param <T> The type of topology (can be a topology or a deployment topology) * @return An instance of TopologyDTO (FIXME Should return an Abstract Topology DTO and renamed as not abstract) */ @ToscaContextual public <T extends Topology> TopologyDTO buildTopologyDTO(T topology) { TopologyDTO topologyDTO = new TopologyDTO(); if (topology != null) { buildAbstractTopologyDTO(topology, topologyDTO); // This contains the value ouf output properties. This has nothing to do with capability somehow.. topologyDTO.setOutputCapabilityProperties(topology.getOutputCapabilityProperties()); } return topologyDTO; } private <T extends Topology> void buildAbstractTopologyDTO(T topology, AbstractTopologyDTO<T> topologyDTO) { topologyDTO.setTopology(topology); topologyDTO.setNodeTypes(getNodeTypes(topology)); topologyDTO.setRelationshipTypes(getRelationshipTypes(topology)); topologyDTO.setCapabilityTypes(getCapabilityTypes(topologyDTO)); topologyDTO.setDataTypes(getDataTypes(topologyDTO)); } private <T extends Topology> Map<String, NodeType> getNodeTypes(T topology) { Map<String, NodeType> types = Maps.newHashMap(); fillTypeMap(NodeType.class, types, topology.getNodeTemplates(), false, false); return types; } private <T extends Topology> Map<String, RelationshipType> getRelationshipTypes(T topology) { Map<String, RelationshipType> types = Maps.newHashMap(); if (topology.getNodeTemplates() != null) { for (NodeTemplate nodeTemplate : topology.getNodeTemplates().values()) { fillTypeMap(RelationshipType.class, types, nodeTemplate.getRelationships(), false, false); } } return types; } private <T extends Topology> Map<String, CapabilityType> getCapabilityTypes(AbstractTopologyDTO<T> topologyDTO) { Map<String, CapabilityType> types = Maps.newHashMap(); Map<String, NodeType> delayedNodeTypeAddMap = Maps.newHashMap(); for (NodeType nodeType : topologyDTO.getNodeTypes().values()) { if (nodeType != null) { for (CapabilityDefinition capabilityDefinition : nodeType.getCapabilities()) { types.put(capabilityDefinition.getType(), ToscaContext.get(CapabilityType.class, capabilityDefinition.getType())); } for (RequirementDefinition requirementDefinition : nodeType.getRequirements()) { CapabilityType capabilityType = ToscaContext.get(CapabilityType.class, requirementDefinition.getType()); if (capabilityType != null) { types.put(requirementDefinition.getType(), capabilityType); } else { // requirements are authorized to be a node type rather than a capability type TODO is it still possible in TOSCA ? NodeType indexedNodeType = ToscaContext.get(NodeType.class, requirementDefinition.getType()); // add it to the actual node types map delayedNodeTypeAddMap.put(requirementDefinition.getType(), indexedNodeType); } } } } for (Map.Entry<String, NodeType> delayedNodeType : delayedNodeTypeAddMap.entrySet()) { topologyDTO.getNodeTypes().put(delayedNodeType.getKey(), delayedNodeType.getValue()); } return types; } private <T extends AbstractInheritableToscaType, V extends AbstractTemplate> void fillTypeMap(Class<T> elementClass, Map<String, T> types, Map<String, V> templateMap, boolean useTemplateNameAsKey, boolean abstractOnly) { if (templateMap == null) { return; } for (Map.Entry<String, V> template : templateMap.entrySet()) { if (!types.containsKey(template.getValue().getType())) { T type = ToscaContext.get(elementClass, template.getValue().getType()); if (!abstractOnly || type.isAbstract()) { String key = useTemplateNameAsKey ? template.getKey() : template.getValue().getType(); types.put(key, type); } } } } private Map<String, DataType> getDataTypes(AbstractTopologyDTO topologyDTO) { Map<String, DataType> indexedDataTypes = Maps.newHashMap(); indexedDataTypes = fillDataTypes(indexedDataTypes, topologyDTO.getNodeTypes()); indexedDataTypes = fillDataTypes(indexedDataTypes, topologyDTO.getRelationshipTypes()); indexedDataTypes = fillDataTypes(indexedDataTypes, topologyDTO.getCapabilityTypes()); return indexedDataTypes; } private <T extends AbstractInheritableToscaType> Map<String, DataType> fillDataTypes(Map<String, DataType> indexedDataTypes, Map<String, T> elements) { for (AbstractInheritableToscaType indexedNodeType : elements.values()) { if (indexedNodeType != null && indexedNodeType.getProperties() != null) { for (PropertyDefinition pd : indexedNodeType.getProperties().values()) { String type = pd.getType(); if (ToscaType.isPrimitive(type) || indexedDataTypes.containsKey(type)) { continue; } DataType dataType = ToscaContext.get(DataType.class, type); if (dataType == null) { dataType = ToscaContext.get(PrimitiveDataType.class, type); } indexedDataTypes.put(type, dataType); } } } return indexedDataTypes; } }