package org.alien4cloud.tosca.editor.processors.relationshiptemplate;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.alien4cloud.tosca.catalog.index.IToscaTypeSearchService;
import org.alien4cloud.tosca.editor.EditionContextManager;
import org.alien4cloud.tosca.editor.exception.CapabilityBoundException;
import org.alien4cloud.tosca.editor.exception.RequirementBoundException;
import org.alien4cloud.tosca.editor.operations.relationshiptemplate.AddRelationshipOperation;
import org.alien4cloud.tosca.editor.processors.nodetemplate.AbstractNodeProcessor;
import org.alien4cloud.tosca.model.definitions.AbstractPropertyValue;
import org.alien4cloud.tosca.model.templates.NodeTemplate;
import org.alien4cloud.tosca.model.templates.RelationshipTemplate;
import org.alien4cloud.tosca.model.templates.Topology;
import org.alien4cloud.tosca.model.types.RelationshipType;
import org.springframework.stereotype.Component;
import com.google.common.collect.Maps;
import alien4cloud.exception.AlreadyExistException;
import alien4cloud.exception.InvalidNameException;
import alien4cloud.exception.NotFoundException;
import alien4cloud.paas.wf.WorkflowsBuilderService;
import alien4cloud.topology.TopologyService;
import alien4cloud.topology.TopologyServiceCore;
import alien4cloud.topology.validation.TopologyCapabilityBoundsValidationServices;
import alien4cloud.topology.validation.TopologyRequirementBoundsValidationServices;
import alien4cloud.tosca.topology.NodeTemplateBuilder;
import alien4cloud.utils.AlienUtils;
import lombok.extern.slf4j.Slf4j;
/**
*
*/
@Slf4j
@Component
public class AddRelationshipProcessor extends AbstractNodeProcessor<AddRelationshipOperation> {
@Inject
private IToscaTypeSearchService toscaTypeSearchService;
@Resource
private TopologyService topologyService;
@Resource
private WorkflowsBuilderService workflowBuilderService;
@Resource
private TopologyRequirementBoundsValidationServices topologyRequirementBoundsValidationServices;
@Resource
private TopologyCapabilityBoundsValidationServices topologyCapabilityBoundsValidationServices;
@Override
protected void processNodeOperation(AddRelationshipOperation operation, NodeTemplate sourceNode) {
if (operation.getRelationshipName() == null || operation.getRelationshipName().isEmpty()) {
throw new InvalidNameException("relationshipName", operation.getRelationshipName(), "Not null or empty");
}
if (AlienUtils.safe(sourceNode.getRelationships()).containsKey(operation.getRelationshipName())) {
throw new AlreadyExistException("Relationship " + operation.getRelationshipName() + " already exist on node " + operation.getNodeName());
}
if (sourceNode.getRequirements() == null || sourceNode.getRequirements().get(operation.getRequirementName()) == null) {
throw new NotFoundException(
"Unable to find requirement with name <" + operation.getRequirementName() + "> on the source node" + operation.getNodeName());
}
Topology topology = EditionContextManager.getTopology();
Map<String, NodeTemplate> nodeTemplates = TopologyServiceCore.getNodeTemplates(topology);
// ensure that the target node exists
TopologyServiceCore.getNodeTemplate(topology.getId(), operation.getTarget(), nodeTemplates);
// We don't use the tosca context as the relationship type may not be in dependencies yet (that's why we use the load type below).
RelationshipType indexedRelationshipType = toscaTypeSearchService.find(RelationshipType.class, operation.getRelationshipType(),
operation.getRelationshipVersion());
if (indexedRelationshipType == null) {
throw new NotFoundException(RelationshipType.class.getName(), operation.getRelationshipType() + ":" + operation.getRelationshipVersion(),
"Unable to find relationship type to create template in topology.");
}
boolean upperBoundReachedSource = topologyRequirementBoundsValidationServices.isRequirementUpperBoundReachedForSource(sourceNode,
operation.getRequirementName(), topology.getDependencies());
if (upperBoundReachedSource) {
// throw exception here
throw new RequirementBoundException(operation.getNodeName(), operation.getRequirementName());
}
boolean upperBoundReachedTarget = topologyCapabilityBoundsValidationServices.isCapabilityUpperBoundReachedForTarget(operation.getTarget(),
nodeTemplates, operation.getTargetedCapabilityName(), topology.getDependencies());
// return with a rest response error
if (upperBoundReachedTarget) {
throw new CapabilityBoundException(operation.getTarget(), operation.getTargetedCapabilityName());
}
// FIXME impact ToscaContext
topologyService.loadType(topology, indexedRelationshipType);
Map<String, RelationshipTemplate> relationships = sourceNode.getRelationships();
if (relationships == null) {
relationships = Maps.newHashMap();
sourceNode.setRelationships(relationships);
}
RelationshipTemplate relationshipTemplate = new RelationshipTemplate();
relationshipTemplate.setName(operation.getRelationshipName());
relationshipTemplate.setTarget(operation.getTarget());
relationshipTemplate.setTargetedCapabilityName(operation.getTargetedCapabilityName());
relationshipTemplate.setRequirementName(operation.getRequirementName());
relationshipTemplate.setRequirementType(sourceNode.getRequirements().get(operation.getRequirementName()).getType());
relationshipTemplate.setType(indexedRelationshipType.getElementId());
relationshipTemplate.setArtifacts(newLinkedHashMap(indexedRelationshipType.getArtifacts()));
relationshipTemplate.setAttributes(newLinkedHashMap(indexedRelationshipType.getAttributes()));
Map<String, AbstractPropertyValue> properties = new LinkedHashMap<String, AbstractPropertyValue>();
NodeTemplateBuilder.fillProperties(properties, indexedRelationshipType.getProperties(), null);
relationshipTemplate.setProperties(properties);
relationships.put(operation.getRelationshipName(), relationshipTemplate);
WorkflowsBuilderService.TopologyContext topologyContext = workflowBuilderService.buildTopologyContext(topology);
workflowBuilderService.addRelationship(topologyContext, operation.getNodeName(), operation.getRelationshipName());
log.debug("Added relationship to the topology [" + topology.getId() + "], node name [" + operation.getNodeName() + "], relationship name ["
+ operation.getRelationshipName() + "]");
}
private <T, V> Map<T, V> newLinkedHashMap(Map<T, V> from) {
if (from == null) {
return new LinkedHashMap<>();
}
return new LinkedHashMap<>(from);
}
}