/* * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 * ============LICENSE_END========================================================= */ package org.openecomp.sdc.be.components.impl; import static org.openecomp.sdc.be.components.impl.ImportUtils.Constants.QUOTE; import static org.openecomp.sdc.be.utils.PropertyFilterConstraintDataDefinitionHelper.createToscaFunctionFromLegacyConstraintValue; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.ACTIVITIES; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.DEFAULT; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.DESCRIPTION; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.FILTERS; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.IMPLEMENTATION; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.INPUTS; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.MILESTONES; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.NOTIFICATIONS; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.OPERATIONS; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.REQUIRED; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.STATUS; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.TYPE; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.VALUE; import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.WORKFLOW; import com.google.gson.Gson; import fj.data.Either; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; import org.apache.commons.collections.MapUtils; import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum; import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException; import org.openecomp.sdc.be.dao.api.ActionStatus; import org.openecomp.sdc.be.datatypes.elements.ActivityDataDefinition; import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition; import org.openecomp.sdc.be.datatypes.elements.FilterDataDefinition; import org.openecomp.sdc.be.datatypes.elements.InputDataDefinition; import org.openecomp.sdc.be.datatypes.elements.ListDataDefinition; import org.openecomp.sdc.be.datatypes.elements.MilestoneDataDefinition; import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition; import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition; import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition; import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition; import org.openecomp.sdc.be.datatypes.elements.ToscaFunction; import org.openecomp.sdc.be.datatypes.elements.ToscaFunctionType; import org.openecomp.sdc.be.datatypes.enums.ConstraintType; import org.openecomp.sdc.be.model.InputDefinition; import org.openecomp.sdc.be.model.InterfaceDefinition; import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder; import org.openecomp.sdc.be.model.tosca.ToscaPropertyType; import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil; import org.openecomp.sdc.exception.ResponseFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Handles interface definition TOSCA conversions */ @Component("interfaceDefinitionHandler") public class InterfaceDefinitionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(InterfaceDefinitionHandler.class); private static final String WITH_ATTRIBUTE = "with attribute '{}': '{}'"; private final InterfaceOperationBusinessLogic interfaceOperationBusinessLogic; public InterfaceDefinitionHandler(final InterfaceOperationBusinessLogic interfaceOperationBusinessLogic) { this.interfaceOperationBusinessLogic = interfaceOperationBusinessLogic; } /** * Creates an interface definition based on a TOSCA map representing an interface definition. * * @param interfaceDefinitionToscaMap the TOSCA interface definition structure * @return an interface definition representation */ public InterfaceDefinition create(final Map interfaceDefinitionToscaMap, final String model) { final InterfaceDefinition interfaceDefinition = new InterfaceDefinition(); interfaceDefinition.setModel(model); if (interfaceDefinitionToscaMap.containsKey(TYPE.getElementName())) { final Object typeObj = interfaceDefinitionToscaMap.get(TYPE.getElementName()); if (!(typeObj instanceof String)) { throw new ByActionStatusComponentException(ActionStatus.INVALID_YAML); } final String type = (String) typeObj; interfaceDefinition.setType(type); interfaceDefinition.setUniqueId(type); } final Map inputDefinitionMap = handleInputs(interfaceDefinitionToscaMap); if (!inputDefinitionMap.isEmpty()) { final Map collect = inputDefinitionMap.entrySet().stream() .collect(Collectors.toMap(Entry::getKey, value -> new InputDataDefinition(value.getValue()))); interfaceDefinition.setInputs(collect); } final Map operationMap; if (interfaceDefinitionToscaMap.containsKey(OPERATIONS.getElementName()) || interfaceDefinitionToscaMap .containsKey(NOTIFICATIONS.getElementName())) { operationMap = handleOperations(interfaceDefinitionToscaMap); //TODO handle notifications } else { operationMap = handleLegacyOperations(interfaceDefinitionToscaMap); } if (!operationMap.isEmpty()) { validateOperations(interfaceDefinition.getType(), operationMap, model); interfaceDefinition.setOperations(operationMap); } return interfaceDefinition; } private void validateOperations(final String interfaceType, final Map operationMap, final String model) { if (MapUtils.isEmpty(operationMap)) { return; } Either, ResponseFormat> interfaceDefinitionMapEither = interfaceOperationBusinessLogic .getAllInterfaceLifecycleTypes(model); if (interfaceDefinitionMapEither.isRight() || MapUtils.isEmpty(interfaceDefinitionMapEither.left().value())) { throw new ByActionStatusComponentException(ActionStatus.INTERFACE_UNKNOWN, interfaceType); } final Map interfaceDefinitionMap = interfaceDefinitionMapEither.left().value(); final Optional interfaceDefinitionOptional = interfaceDefinitionMap.entrySet().stream() .filter(interfaceDefinitionEntry -> interfaceDefinitionEntry.getKey() .equalsIgnoreCase(UniqueIdBuilder.buildInterfaceTypeUid(model, interfaceType))).map(Entry::getValue).findFirst(); if (interfaceDefinitionOptional.isEmpty()) { throw new ByActionStatusComponentException(ActionStatus.INTERFACE_UNKNOWN, interfaceType); } final InterfaceDefinition interfaceDefinition = interfaceDefinitionOptional.get(); operationMap.keySet().forEach(operation1 -> { if (!interfaceDefinition.hasOperation(operation1)) { throw new ByActionStatusComponentException(ActionStatus.INTERFACE_OPERATION_NOT_DEFINED, operation1, interfaceType); } }); } private Map handleOperations(final Map interfaceDefinitionToscaMap) { if (!interfaceDefinitionToscaMap.containsKey(OPERATIONS.getElementName())) { return Collections.emptyMap(); } final Map operationMap = (Map) interfaceDefinitionToscaMap.get(OPERATIONS.getElementName()); return operationMap.entrySet().stream() .map(interfaceEntry -> createOperation(interfaceEntry.getKey(), (Map) interfaceEntry.getValue())) .collect(Collectors.toMap(OperationDataDefinition::getName, operationDataDefinition -> operationDataDefinition)); } private Map handleLegacyOperations(final Map interfaceDefinitionToscaMap) { final List notALegacyOperationEntry = Arrays .asList(OPERATIONS.getElementName(), TYPE.getElementName(), INPUTS.getElementName(), NOTIFICATIONS.getElementName()); return interfaceDefinitionToscaMap.entrySet().stream().filter(interfaceEntry -> !notALegacyOperationEntry.contains(interfaceEntry.getKey())) .map(interfaceEntry -> createOperation(interfaceEntry.getKey(), (Map) interfaceEntry.getValue())) .collect(Collectors.toMap(OperationDataDefinition::getName, operationDataDefinition -> operationDataDefinition)); } private OperationDataDefinition createOperation(final String operationName, final Map operationDefinitionMap) { final OperationDataDefinition operation = new OperationDataDefinition(); operation.setUniqueId(UUID.randomUUID().toString()); operation.setName(operationName); if (MapUtils.isEmpty(operationDefinitionMap)) { return operation; } Object operationDescription = operationDefinitionMap.get(DESCRIPTION.getElementName()); if (null != operationDescription) { operation.setDescription(operationDescription.toString()); } operation.setImplementation(handleOperationImplementation(operationDefinitionMap).orElse(new ArtifactDataDefinition())); if (operationDefinitionMap.containsKey(INPUTS.getElementName())) { final Map interfaceInputs = (Map) operationDefinitionMap.get(INPUTS.getElementName()); operation.setInputs(handleInterfaceOperationInputs(interfaceInputs)); } if (operationDefinitionMap.containsKey(MILESTONES.getElementName())) { final Map interfaceMilestones = (Map) operationDefinitionMap.get(MILESTONES.getElementName()); operation.setMilestones(handleInterfaceOperationMilestones(interfaceMilestones)); } return operation; } private Map handleInterfaceOperationMilestones(final Map interfaceMilestones) { final Map milestones = new HashMap<>(); for (final Entry interfaceInput : interfaceMilestones.entrySet()) { final MilestoneDataDefinition operationMilestone = new MilestoneDataDefinition(); ListDataDefinition activities = handleMilestoneActivities(interfaceInput.getValue()); if (activities.isEmpty()) { throw new ByActionStatusComponentException(ActionStatus.INVALID_OPERATION_MILESTONE, interfaceInput.getKey()); } ListDataDefinition filters = handleMilestoneFilters(interfaceInput.getValue()); if (!filters.isEmpty()) { operationMilestone.setFilters(filters); } operationMilestone.setActivities(activities); milestones.put(interfaceInput.getKey(), operationMilestone); } return milestones; } private ListDataDefinition handleMilestoneFilters(Object milestone) { ListDataDefinition filters = new ListDataDefinition<>(); if (milestone instanceof Map) { final LinkedHashMap milestoneValue = (LinkedHashMap) milestone; if (milestoneValue.containsKey(FILTERS.getElementName())) { final List milestoneFilters = (List) milestoneValue.get(FILTERS.getElementName()); for (Object filtersValues : milestoneFilters) { if (filtersValues instanceof Map) { FilterDataDefinition filter = new FilterDataDefinition(); Map filterMap = (Map) filtersValues; Optional> filterOptional = filterMap.entrySet().stream().filter(entrySet -> entrySet.getValue() instanceof Map).findAny(); if (filterOptional.isEmpty()) { continue; } Entry filterValue = filterOptional.get(); if (!(filterValue.getValue() instanceof Map)) { continue; } Map valueMap = (Map) filterValue.getValue(); Optional constraintTypeOptional = valueMap.keySet().stream().filter(key -> ConstraintType.findByType(key).isPresent()).findAny(); if (constraintTypeOptional.isEmpty()) { continue; } String constraintType = constraintTypeOptional.get(); filter.setName(filterValue.getKey()); filter.setConstraint(constraintType); Object value = valueMap.get(constraintType); if (value instanceof Map) { Map valueAsMap = (Map) value; Optional toscaFunctionTypeOptional = valueAsMap.keySet().stream().filter(key -> ToscaFunctionType.findType(key).isPresent()).findAny(); if (toscaFunctionTypeOptional.isPresent()) { Optional toscaValue = createToscaFunctionFromLegacyConstraintValue(valueAsMap); if (toscaValue.isPresent()) { filter.setToscaFunction(toscaValue.get()); } } } filter.setFilterValue(value); filters.add(filter); } else { return new ListDataDefinition<>(); } } } else { return new ListDataDefinition<>(); } } return filters; } private ListDataDefinition handleMilestoneActivities(final Object value) { ListDataDefinition activities = new ListDataDefinition<>(); if (value instanceof Map) { final LinkedHashMap activitiesValue = (LinkedHashMap) value; if (activitiesValue.containsKey(ACTIVITIES.getElementName())) { final List milestoneActivities = (List) activitiesValue.get(ACTIVITIES.getElementName()); for (Object activityValue : milestoneActivities) { ActivityDataDefinition activity = new ActivityDataDefinition(); if (activityValue instanceof Map) { Map activityMap = (Map) activityValue; if (activityMap.containsKey(INPUTS.getElementName())) { activity.setInputs(handleInterfaceOperationInputs((Map) activityMap.get(INPUTS.getElementName()))); } if (activityMap.containsKey(TYPE.getElementName()) && activityMap.containsKey(WORKFLOW.getElementName())) { activity.setType((String) activityMap.get(TYPE.getElementName())); activity.setWorkflow((String) activityMap.get(WORKFLOW.getElementName())); activities.add(activity); } else { return new ListDataDefinition<>(); } } else { return new ListDataDefinition<>(); } } } else { return new ListDataDefinition<>(); } } return activities; } private ListDataDefinition handleInterfaceOperationInputs(final Map interfaceInputs) { final ListDataDefinition inputs = new ListDataDefinition<>(); for (final Entry interfaceInput : interfaceInputs.entrySet()) { final OperationInputDefinition operationInput = new OperationInputDefinition(); operationInput.setUniqueId(UUID.randomUUID().toString()); operationInput.setInputId(operationInput.getUniqueId()); operationInput.setName(interfaceInput.getKey()); handleInputToscaDefinition(interfaceInput.getKey(), interfaceInput.getValue(), operationInput); inputs.add(operationInput); } return inputs; } private void handleInputToscaDefinition(final String inputName, final Object value, final OperationInputDefinition operationInput) { if (value instanceof Map) { final LinkedHashMap inputPropertyValue = (LinkedHashMap) value; LOGGER.debug("Creating interface operation input '{}'", inputName); if (inputPropertyValue.get(TYPE.getElementName()) != null) { final String type = inputPropertyValue.get(TYPE.getElementName()).toString(); LOGGER.debug(WITH_ATTRIBUTE, TYPE.getElementName(), type); operationInput.setType(type); } if (inputPropertyValue.get(DESCRIPTION.getElementName()) != null) { final String description = inputPropertyValue.get(DESCRIPTION.getElementName()).toString(); LOGGER.debug(WITH_ATTRIBUTE, DESCRIPTION.getElementName(), description); operationInput.setDescription(description); } if (inputPropertyValue.get(REQUIRED.getElementName()) != null) { final boolean required = Boolean.parseBoolean(inputPropertyValue.get(REQUIRED.getElementName()).toString()); LOGGER.debug(WITH_ATTRIBUTE, REQUIRED.getElementName(), required); operationInput.setRequired(required); } if (inputPropertyValue.get(DEFAULT.getElementName()) != null) { final Gson gson = new Gson(); final String json = gson.toJson(inputPropertyValue.get(DEFAULT.getElementName())); LOGGER.debug(WITH_ATTRIBUTE, DEFAULT.getElementName(), json); operationInput.setToscaDefaultValue(json); } if (inputPropertyValue.get(VALUE.getElementName()) != null) { final Gson gson = new Gson(); final String json = gson.toJson(inputPropertyValue.get(VALUE.getElementName())); operationInput.setValue(json); } if (inputPropertyValue.get(STATUS.getElementName()) != null) { final String status = inputPropertyValue.get(STATUS.getElementName()).toString(); LOGGER.debug(WITH_ATTRIBUTE, STATUS.getElementName(), status); operationInput.setStatus(status); } return; } if (value instanceof String) { final String stringValue = (String) value; operationInput.setDefaultValue(stringValue); operationInput.setToscaDefaultValue(stringValue); operationInput.setValue(stringValue); } } @SuppressWarnings("unchecked") private Optional handleOperationImplementation(final Map operationDefinitionMap) { if (!operationDefinitionMap.containsKey(IMPLEMENTATION.getElementName())) { return Optional.empty(); } final ArtifactDataDefinition artifactDataDefinition = new ArtifactDataDefinition(); if (operationDefinitionMap.get(IMPLEMENTATION.getElementName()) instanceof Map && ((Map) operationDefinitionMap.get(IMPLEMENTATION.getElementName())).containsKey("primary")) { Map implDetails = (Map) ((Map) operationDefinitionMap.get(IMPLEMENTATION.getElementName())).get("primary"); if (implDetails.get("file") != null) { final String file = implDetails.get("file").toString(); artifactDataDefinition.setArtifactName(generateArtifactName(file)); } if (implDetails.get("type") != null) { artifactDataDefinition.setArtifactType(implDetails.get("type").toString()); } if (implDetails.get("artifact_version") != null) { artifactDataDefinition.setArtifactVersion(implDetails.get("artifact_version").toString()); } if (implDetails.get("properties") instanceof Map) { List operationProperties = artifactDataDefinition.getProperties() == null ? new ArrayList<>() : artifactDataDefinition.getProperties(); Map properties = (Map) implDetails.get("properties"); properties.forEach((k, v) -> { ToscaPropertyType type = getTypeFromObject(v); if (type != null) { PropertyDataDefinition propertyDef = new PropertyDataDefinition(); propertyDef.setName(k); propertyDef.setType(type.getType()); propertyDef.setValue(v.toString()); if (type.equals(ToscaPropertyType.LIST)) { Gson gson = new Gson(); propertyDef.setValue(gson.toJson(v)); PropertyDataDefinition pdd = new PropertyDataDefinition(); pdd.setType("string"); SchemaDefinition sd = new SchemaDefinition(); sd.setProperty(pdd); propertyDef.setSchema(sd); } artifactDataDefinition.addProperty(propertyDef); } }); } } if (operationDefinitionMap.get(IMPLEMENTATION.getElementName()) instanceof Map && ((Map) operationDefinitionMap.get(IMPLEMENTATION.getElementName())).containsKey("timeout")) { final Object timeOut = ((Map) operationDefinitionMap.get(IMPLEMENTATION.getElementName())).get("timeout"); artifactDataDefinition.setTimeout((Integer)timeOut); } if (operationDefinitionMap.get(IMPLEMENTATION.getElementName()) instanceof String) { final String implementation = (String) operationDefinitionMap.get(IMPLEMENTATION.getElementName()); artifactDataDefinition.setArtifactName(generateArtifactName(implementation)); } return Optional.of(artifactDataDefinition); } private String generateArtifactName(final String name) { if (OperationArtifactUtil.artifactNameIsALiteralValue(name)) { return name; } else { return QUOTE + name + QUOTE; } } private ToscaPropertyType getTypeFromObject(final Object value) { if (value instanceof String) { return ToscaPropertyType.STRING; } if (value instanceof Integer) { return ToscaPropertyType.INTEGER; } if (value instanceof Boolean) { return ToscaPropertyType.BOOLEAN; } if (value instanceof Float || value instanceof Double) { return ToscaPropertyType.FLOAT; } if (value instanceof List) { return ToscaPropertyType.LIST; } return null; } private Map handleInputs(final Map interfaceDefinitionToscaMap) { if (!interfaceDefinitionToscaMap.containsKey(INPUTS.getElementName())) { return Collections.emptyMap(); } final Either, ResultStatusEnum> inputMapEither = ImportUtils.getInputs(interfaceDefinitionToscaMap); if (inputMapEither.isRight()) { return Collections.emptyMap(); } return inputMapEither.left().value(); } }