2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 Nordix Foundation
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 * SPDX-License-Identifier: Apache-2.0
17 * ============LICENSE_END=========================================================
20 package org.openecomp.sdc.be.model.operations.impl;
22 import fj.data.Either;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Optional;
29 import java.util.stream.Collectors;
30 import org.apache.commons.lang3.StringUtils;
31 import org.apache.commons.lang3.tuple.ImmutablePair;
32 import org.openecomp.sdc.be.dao.api.ActionStatus;
33 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
34 import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
35 import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
36 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
37 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
38 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
39 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
40 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
41 import org.openecomp.sdc.be.model.ArtifactTypeDefinition;
42 import org.openecomp.sdc.be.model.PropertyDefinition;
43 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.ModelOperationExceptionSupplier;
44 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException;
45 import org.openecomp.sdc.be.model.operations.api.DerivedFromOperation;
46 import org.openecomp.sdc.be.model.operations.api.IArtifactTypeOperation;
47 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
48 import org.openecomp.sdc.be.resources.data.ArtifactTypeData;
49 import org.openecomp.sdc.be.resources.data.ModelData;
50 import org.openecomp.sdc.be.resources.data.PropertyData;
51 import org.openecomp.sdc.be.resources.data.UniqueIdData;
52 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
53 import org.openecomp.sdc.common.log.wrappers.Logger;
54 import org.springframework.beans.factory.annotation.Autowired;
55 import org.springframework.stereotype.Component;
58 * This class is responsible for handling all operations for the TOSCA artifact types
60 @Component("artifact-type-operation")
61 public class ArtifactTypeOperation implements IArtifactTypeOperation {
63 private static final Logger LOGGER = Logger.getLogger(ArtifactTypeOperation.class.getName());
65 private final JanusGraphGenericDao janusGraphGenericDao;
66 private final PropertyOperation propertyOperation;
67 private final DerivedFromOperation derivedFromOperation;
68 private final ModelOperation modelOperation;
71 public ArtifactTypeOperation(final JanusGraphGenericDao janusGraphGenericDao,
72 final PropertyOperation propertyOperation,
73 final DerivedFromOperation derivedFromOperation,
74 final ModelOperation modelOperation) {
75 this.janusGraphGenericDao = janusGraphGenericDao;
76 this.propertyOperation = propertyOperation;
77 this.derivedFromOperation = derivedFromOperation;
78 this.modelOperation = modelOperation;
82 * Creates a TOSCA artifact types
83 * @param artifactType the TOSCA artifact types definition to be created
84 * @return the created TOSCA artifact types definition
87 public ArtifactTypeDefinition createArtifactType(final ArtifactTypeDefinition artifactType) {
88 return createArtifactType(artifactType, false);
92 public ArtifactTypeDefinition createArtifactType(final ArtifactTypeDefinition artifactType,
93 final boolean inTransaction) {
94 Either<ArtifactTypeData, JanusGraphOperationStatus> createNodeResult = null;
96 artifactType.setUniqueId(UniqueIdBuilder.buildArtifactTypeUid(artifactType.getModel(), artifactType.getType()));
97 final ArtifactTypeData artifactTypeData = new ArtifactTypeData(artifactType);
98 final Either<ArtifactTypeData, JanusGraphOperationStatus> existArtifact = janusGraphGenericDao
99 .getNode(artifactTypeData.getUniqueIdKey(), artifactTypeData.getUniqueId(), ArtifactTypeData.class);
100 if (!existArtifact.isLeft()) {
101 createNodeResult = janusGraphGenericDao.createNode(artifactTypeData, ArtifactTypeData.class);
102 if (createNodeResult.isRight()) {
103 final JanusGraphOperationStatus operationStatus = createNodeResult.right().value();
104 LOGGER.error(EcompLoggerErrorCode.DATA_ERROR,
105 "Failed to add artifact type {} to graph. Operation status {}", artifactType.getType(), operationStatus);
106 throw new OperationException(ActionStatus.GENERAL_ERROR,
107 String.format("Failed to create artifact type %s with status %s", artifactType.getType(),
108 DaoStatusConverter.convertJanusGraphStatusToStorageStatus(operationStatus)));
110 addPropertiesToArtifactType(artifactType);
111 addModelRelation(artifactType);
112 addDerivedFromRelation(artifactType);
113 return convertArtifactDataDefinition(createNodeResult.left().value());
115 LOGGER.debug("Artifact type already exist {}", artifactType);
116 return convertArtifactDataDefinition(existArtifact.left().value());
119 if (!inTransaction) {
120 if (createNodeResult == null || createNodeResult.isRight()) {
121 LOGGER.debug("Rollback on graph.");
122 janusGraphGenericDao.rollback();
124 janusGraphGenericDao.commit();
131 * Finds all TOSCA artifact types applicable to the given model name
132 * @param model model name
133 * @return all the TOSCA artifact types found
136 public Map<String, ArtifactTypeDefinition> getAllArtifactTypes(final String model) {
137 final Either<List<ArtifactTypeData>, JanusGraphOperationStatus> artifactTypes =
138 janusGraphGenericDao.getByCriteriaForModel(NodeTypeEnum.ArtifactType, Collections.emptyMap(), model, ArtifactTypeData.class);
139 if (artifactTypes.isRight()) {
140 if (JanusGraphOperationStatus.NOT_FOUND == artifactTypes.right().value()) {
141 return Collections.emptyMap();
143 throw new OperationException(ActionStatus.GENERAL_ERROR,
144 String.format("Failed to find artifact on JanusGraph with status %s",
145 DaoStatusConverter.convertJanusGraphStatusToStorageStatus(artifactTypes.right().value())));
147 final Map<String, ArtifactTypeDefinition> artifactTypeDefinitionTypes = new HashMap<>();
148 final List<ArtifactTypeDefinition> artifactTypeList = artifactTypes.left().value().stream()
149 .map(this::convertArtifactDataDefinition)
150 .filter(artifactTypeDefinition -> artifactTypeDefinition.getUniqueId().equalsIgnoreCase(UniqueIdBuilder
151 .buildArtifactTypeUid(artifactTypeDefinition.getModel(), artifactTypeDefinition.getType())))
152 .collect(Collectors.toList());
153 for (final ArtifactTypeDefinition type : artifactTypeList) {
154 artifactTypeDefinitionTypes.put(type.getUniqueId(), type);
156 return artifactTypeDefinitionTypes;
160 * Coverts the given artifact type data into a TOSCA artifact types definition
161 * @param artifactTypeData artifact type data representation
162 * @return the TOSCA artifact types definition
164 private ArtifactTypeDefinition convertArtifactDataDefinition(final ArtifactTypeData artifactTypeData) {
165 LOGGER.debug("The object returned after create tosca artifact type is {}", artifactTypeData);
166 final ArtifactTypeDefinition artifactType = new ArtifactTypeDefinition(artifactTypeData.getArtifactTypeDataDefinition());
167 final var modelAssociatedToArtifactOptional = getModelAssociatedToArtifact(artifactTypeData.getUniqueId());
168 if (!modelAssociatedToArtifactOptional.isEmpty()) {
169 artifactType.setModel(modelAssociatedToArtifactOptional.get());
171 artifactType.setType(artifactTypeData.getArtifactTypeDataDefinition().getType());
172 final ArtifactTypeData derivedFromNode = fillDerivedFrom(artifactType);
173 fillProperties(artifactType, derivedFromNode);
178 * Finds an artifact type data on JanusGraph based on the given parameters
179 * @param type the artifact type derived from
180 * @param model the model name
181 * @return the optional artifact type data found
183 private Optional<ArtifactTypeData> getLatestArtifactTypeByType(final String type, final String model) {
184 final Map<String, Object> mapCriteria = new HashMap<>();
185 mapCriteria.put(GraphPropertiesDictionary.TYPE.getProperty(), type);
186 final Either<List<ArtifactTypeData>, JanusGraphOperationStatus> result = janusGraphGenericDao.getByCriteriaForModel(NodeTypeEnum.ArtifactType,
187 mapCriteria, model, ArtifactTypeData.class);
188 if (result.isRight()) {
189 final JanusGraphOperationStatus status = result.right().value();
190 if (JanusGraphOperationStatus.NOT_FOUND == status) {
191 return Optional.empty();
193 throw new OperationException(ActionStatus.GENERAL_ERROR,
194 String.format("Failed to find artifact by type on JanusGraph with status %s",
195 DaoStatusConverter.convertJanusGraphStatusToStorageStatus(status)));
197 return Optional.of(result.left().value().get(0));
201 * Creates the derivedFrom relation for the given TOSCA artifact types
202 * @param artifactType the TOSCA artifact types definition
204 private void addDerivedFromRelation(final ArtifactTypeDefinition artifactType) {
205 final String derivedFrom = artifactType.getDerivedFrom();
206 final String artifactId = artifactType.getUniqueId();
207 if (derivedFrom.isEmpty()) {
210 final var getArtifactTypeOptional = getLatestArtifactTypeByType(derivedFrom, artifactType.getModel());
211 if (getArtifactTypeOptional.isPresent()) {
212 if (derivedFromOperation.addDerivedFromRelation(artifactId, getArtifactTypeOptional.get().getUniqueId(),
213 NodeTypeEnum.ArtifactType).isRight()) {
214 throw new OperationException(ActionStatus.GENERAL_ERROR,
215 String.format("Failed creating derivedFrom relation for artifact type %s", artifactType.getType()));
221 * Adds a property definition to the given TOSCA artifact types definition
222 * @param artifactType the TOSCA artifact types
224 private void addPropertiesToArtifactType(final ArtifactTypeDefinition artifactType) {
225 final List<PropertyDefinition> properties = artifactType.getProperties();
226 final Either<Map<String, PropertyData>, JanusGraphOperationStatus> addPropertiesToArtifactType =
227 propertyOperation.addPropertiesToElementType(artifactType.getUniqueId(), NodeTypeEnum.ArtifactType, properties);
228 if (addPropertiesToArtifactType.isRight()) {
229 throw new OperationException(ActionStatus.GENERAL_ERROR,
230 String.format("Failed creating properties for artifact type %s with status %s",
231 artifactType.getType(), DaoStatusConverter.convertJanusGraphStatusToStorageStatus(addPropertiesToArtifactType.right().value())));
236 * Creates an edge between the given TOSCA artifact types and it`s model
237 * @param artifactType the TOSCA artifact types
239 private void addModelRelation(final ArtifactTypeDefinition artifactType) {
240 final String model = artifactType.getModel();
241 if (StringUtils.isNotEmpty(model)) {
242 final GraphNode from = new UniqueIdData(NodeTypeEnum.Model, UniqueIdBuilder.buildModelUid(model));
243 final GraphNode to = new UniqueIdData(NodeTypeEnum.ArtifactType, artifactType.getUniqueId());
244 LOGGER.info("Connecting model {} to type {}", from, to);
245 final Either<GraphRelation, JanusGraphOperationStatus> status = janusGraphGenericDao.createRelation(from,
246 to, GraphEdgeLabels.MODEL_ELEMENT, Collections.emptyMap());
247 if (status.isRight()) {
248 throw new OperationException(ActionStatus.GENERAL_ERROR,
249 String.format("Failed creating relation between model %s and artifact type %s with status %s",
250 model, artifactType.getType(), DaoStatusConverter.convertJanusGraphStatusToStorageStatus(status.right().value())));
256 * Finds a model associated to the given artifact type unique id
257 * @param uid the TOSCA artifact types unique id
260 private Optional<String> getModelAssociatedToArtifact(final String uid) {
261 final Either<ImmutablePair<ModelData, GraphEdge>, JanusGraphOperationStatus> model =
262 janusGraphGenericDao.getParentNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Interface), uid,
263 GraphEdgeLabels.MODEL_ELEMENT, NodeTypeEnum.Model, ModelData.class);
264 if (model.isLeft()) {
265 return Optional.ofNullable(model.left().value().getLeft().getName());
267 return Optional.empty();
271 * Finds the derived from for teh given TOSCA artifact types
272 * @param artifactType
275 private ArtifactTypeData fillDerivedFrom(final ArtifactTypeDefinition artifactType) {
276 final Either<ArtifactTypeData, StorageOperationStatus> result = derivedFromOperation
277 .getDerivedFromChild(artifactType.getUniqueId(), NodeTypeEnum.ArtifactType, ArtifactTypeData.class)
278 .right().bind(this::handleDerivedFromNotExist).left()
279 .map(derivedFrom -> setDerivedFrom(artifactType, derivedFrom));
280 if (result.isRight()) {
281 throw new OperationException(ActionStatus.GENERAL_ERROR,
282 String.format("Failed fetching derivedFrom for artifact type %s with status %s",
283 artifactType.getType(), result.right().value()));
285 return result.left().value();
288 private Either<ArtifactTypeData, StorageOperationStatus> handleDerivedFromNotExist(final StorageOperationStatus storageOperationStatus) {
289 if (storageOperationStatus == StorageOperationStatus.NOT_FOUND) {
290 return Either.left(null);
292 return Either.right(storageOperationStatus);
295 private ArtifactTypeData setDerivedFrom(final ArtifactTypeDefinition artifactType, final ArtifactTypeData derivedFrom) {
296 if (derivedFrom != null) {
297 artifactType.setDerivedFrom(derivedFrom.getArtifactTypeDataDefinition().getType());
303 * Finds all properties for the given TOSCA artifact types
304 * @param artifactType the TOSCA artifact types
305 * @param derivedFromNode the TOSCA artifact types derived from
307 private void fillProperties(final ArtifactTypeDefinition artifactType, final ArtifactTypeData derivedFromNode) {
308 final Either<List<PropertyDefinition>, StorageOperationStatus> result =
309 propertyOperation.findPropertiesOfNode(NodeTypeEnum.ArtifactType, artifactType.getUniqueId()).right()
310 .bind(this::handleNoProperties).left().bind(propsMap -> fillDerivedFromProperties(artifactType,
311 derivedFromNode, new ArrayList<>(propsMap.values())));
312 if (result.isRight()) {
313 throw new OperationException(ActionStatus.GENERAL_ERROR,
314 String.format("Failed fetching properties for artifact type %s with status %s",
315 artifactType.getType(), result.right().value()));
319 private Either<Map<String, PropertyDefinition>, StorageOperationStatus> handleNoProperties(
320 final JanusGraphOperationStatus janusGraphOperationStatus) {
321 if (janusGraphOperationStatus == JanusGraphOperationStatus.NOT_FOUND) {
322 return Either.left(new HashMap<>());
324 return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(janusGraphOperationStatus));
327 private Either<List<PropertyDefinition>, StorageOperationStatus> fillDerivedFromProperties(final ArtifactTypeDefinition artifactType,
328 final ArtifactTypeData derivedFromNode,
329 final List<PropertyDefinition> artifactTypeProperties)
331 if (derivedFromNode == null) {
332 artifactType.setProperties(artifactTypeProperties);
333 return Either.left(artifactTypeProperties);
335 return propertyOperation
336 .getAllPropertiesRec(derivedFromNode.getUniqueId(), NodeTypeEnum.ArtifactType, ArtifactTypeData.class)
337 .left().map(derivedFromProps -> {
338 artifactTypeProperties.addAll(derivedFromProps);
339 return artifactTypeProperties;
340 }).left().map(allProps -> {
341 artifactType.setProperties(allProps);
347 * The Model field is an optional entry when uploading a resource. If the field is present, it validates if the Model name exists.
348 * @param modelName Model names declared on the resource json representation
350 public void validateModel(final String modelName) {
351 if (modelOperation.findModelByName(modelName).isEmpty()) {
352 LOGGER.error(EcompLoggerErrorCode.DATA_ERROR,"Could not find model name {}", modelName);
353 throw ModelOperationExceptionSupplier.invalidModel(modelName).get();