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=========================================================
19 package org.openecomp.sdc.be.model.operations.impl;
21 import fj.data.Either;
22 import java.nio.charset.StandardCharsets;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.EnumMap;
26 import java.util.HashMap;
27 import java.util.List;
29 import java.util.Objects;
30 import java.util.Optional;
32 import java.util.stream.Collectors;
33 import org.apache.commons.collections.MapUtils;
34 import org.apache.commons.lang3.StringUtils;
35 import org.apache.commons.lang3.tuple.ImmutablePair;
36 import org.onap.sdc.tosca.services.YamlUtil;
37 import org.openecomp.sdc.be.dao.api.ActionStatus;
38 import org.openecomp.sdc.be.dao.cassandra.ToscaModelImportCassandraDao;
39 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
40 import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
41 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
42 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
43 import org.openecomp.sdc.be.dao.jsongraph.GraphVertex;
44 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphDao;
45 import org.openecomp.sdc.be.dao.jsongraph.types.VertexTypeEnum;
46 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
47 import org.openecomp.sdc.be.data.model.ToscaImportByModel;
48 import org.openecomp.sdc.be.datatypes.enums.GraphPropertyEnum;
49 import org.openecomp.sdc.be.datatypes.enums.ModelTypeEnum;
50 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
51 import org.openecomp.sdc.be.model.Model;
52 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.ModelOperationExceptionSupplier;
53 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException;
54 import org.openecomp.sdc.be.model.operations.api.DerivedFromOperation;
55 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
56 import org.openecomp.sdc.be.resources.data.ModelData;
57 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
58 import org.openecomp.sdc.common.log.wrappers.Logger;
59 import org.springframework.beans.factory.annotation.Autowired;
60 import org.springframework.stereotype.Component;
61 import org.yaml.snakeyaml.Yaml;
63 @Component("model-operation")
64 public class ModelOperation {
66 private static final Logger log = Logger.getLogger(ModelOperation.class);
67 private static final String ADDITIONAL_TYPE_DEFINITIONS = "additional_type_definitions.yml";
69 private final JanusGraphGenericDao janusGraphGenericDao;
70 private final JanusGraphDao janusGraphDao;
71 private final ToscaModelImportCassandraDao toscaModelImportCassandraDao;
72 private final DerivedFromOperation derivedFromOperation;
75 public ModelOperation(final JanusGraphGenericDao janusGraphGenericDao,
76 final JanusGraphDao janusGraphDao,
77 final ToscaModelImportCassandraDao toscaModelImportCassandraDao,
78 final DerivedFromOperation derivedFromOperation) {
79 this.janusGraphGenericDao = janusGraphGenericDao;
80 this.janusGraphDao = janusGraphDao;
81 this.toscaModelImportCassandraDao = toscaModelImportCassandraDao;
82 this.derivedFromOperation = derivedFromOperation;
85 public Model createModel(final Model model, final boolean inTransaction) {
87 final var modelData = new ModelData(model.getName(), UniqueIdBuilder.buildModelUid(model.getName()), model.getModelType());
89 final Either<ModelData, JanusGraphOperationStatus> createNode = janusGraphGenericDao.createNode(modelData, ModelData.class);
90 if (createNode.isRight()) {
91 final var janusGraphOperationStatus = createNode.right().value();
92 log.error(EcompLoggerErrorCode.DATA_ERROR, ModelOperation.class.getName(), "Problem while creating model, reason {}",
93 janusGraphOperationStatus);
94 if (janusGraphOperationStatus == JanusGraphOperationStatus.JANUSGRAPH_SCHEMA_VIOLATION) {
95 throw ModelOperationExceptionSupplier.modelAlreadyExists(model.getName()).get();
97 throw new OperationException(ActionStatus.GENERAL_ERROR,
98 String.format("Failed to create model %s on JanusGraph with %s error", model, janusGraphOperationStatus));
100 addDerivedFromRelation(model);
101 result = new Model(createNode.left().value().getName(), model.getDerivedFrom(), model.getModelType());
104 if (!inTransaction) {
105 if (Objects.nonNull(result)) {
106 janusGraphGenericDao.commit();
108 janusGraphGenericDao.rollback();
114 private void addDerivedFromRelation(final Model model) {
115 final String derivedFrom = model.getDerivedFrom();
116 if (derivedFrom == null) {
119 log.debug("Adding derived from relation between model {} to its parent {}",
120 model.getName(), derivedFrom);
121 final Optional<Model> derivedFromModelOptional = this.findModelByName(derivedFrom);
122 if (derivedFromModelOptional.isPresent()) {
123 final Either<GraphRelation, StorageOperationStatus> result = derivedFromOperation.addDerivedFromRelation(
124 UniqueIdBuilder.buildModelUid(model.getName()),
125 UniqueIdBuilder.buildModelUid(derivedFromModelOptional.get().getName()), NodeTypeEnum.Model);
126 if (result.isRight()) {
127 throw new OperationException(ActionStatus.GENERAL_ERROR,
128 String.format("Failed to create relationship from model %s to derived from model %s on JanusGraph with %s error", model,
129 derivedFrom, result.right().value()));
134 public Optional<GraphVertex> findModelVertexByName(final String name) {
135 if (StringUtils.isEmpty(name)) {
136 return Optional.empty();
138 final Map<GraphPropertyEnum, Object> props = new EnumMap<>(GraphPropertyEnum.class);
139 props.put(GraphPropertyEnum.NAME, name);
140 props.put(GraphPropertyEnum.UNIQUE_ID, UniqueIdBuilder.buildModelUid(name));
141 final List<GraphVertex> modelVerticesList = findModelVerticesByCriteria(props);
142 if (modelVerticesList.isEmpty()) {
143 return Optional.empty();
145 return Optional.ofNullable(modelVerticesList.get(0));
148 public Optional<Model> findModelByName(final String name) {
149 if (StringUtils.isEmpty(name)) {
150 return Optional.empty();
152 final Optional<GraphVertex> modelVertexOpt = findModelVertexByName(name);
153 if (modelVertexOpt.isEmpty()) {
154 return Optional.empty();
157 final GraphVertex graphVertex = modelVertexOpt.get();
158 return Optional.of(convertToModel(graphVertex));
161 public void createModelImports(final String modelId, final Map<String, byte[]> zipContent) {
162 if (MapUtils.isEmpty(zipContent)) {
165 final List<ToscaImportByModel> toscaImportByModelList = zipContent.entrySet().stream()
167 final String path = entry.getKey();
168 final byte[] bytes = entry.getValue();
169 final String content = new String(bytes, StandardCharsets.UTF_8);
170 final var toscaImportByModel = new ToscaImportByModel();
171 toscaImportByModel.setModelId(modelId);
172 toscaImportByModel.setFullPath(path);
173 toscaImportByModel.setContent(content);
174 return toscaImportByModel;
175 }).collect(Collectors.toList());
176 toscaModelImportCassandraDao.importAll(modelId, toscaImportByModelList);
180 * Find all the model default imports, with the option to include the default imports from the parent model.
182 * @param modelId the model id
183 * @param includeParent a flag to include the parent model imports.
184 * @return the list of model default imports, or an empty list if no imports were found.
186 public List<ToscaImportByModel> findAllModelImports(final String modelId, final boolean includeParent) {
187 final List<ToscaImportByModel> toscaImportByModelList = toscaModelImportCassandraDao.findAllByModel(modelId);
189 findModelByName(modelId).ifPresent(model -> {
190 if (model.getDerivedFrom() != null) {
191 toscaImportByModelList.addAll(toscaModelImportCassandraDao.findAllByModel(model.getDerivedFrom()));
195 return toscaImportByModelList;
199 * Finds all the models.
201 * @return the list of models
203 public List<Model> findAllModels() {
204 return findModelsByCriteria(Collections.emptyMap());
207 public List<Model> findModels(final ModelTypeEnum modelType) {
208 final Map<GraphPropertyEnum, Object> propertyCriteria = new EnumMap<>(GraphPropertyEnum.class);
209 propertyCriteria.put(GraphPropertyEnum.MODEL_TYPE, modelType.getValue());
211 return findModelsByCriteria(propertyCriteria);
214 private List<Model> findModelsByCriteria(final Map<GraphPropertyEnum, Object> propertyCriteria) {
215 final List<GraphVertex> modelVerticesByCriteria = findModelVerticesByCriteria(propertyCriteria);
216 if (modelVerticesByCriteria.isEmpty()) {
217 return Collections.emptyList();
220 return modelVerticesByCriteria.stream().map(this::convertToModel).collect(Collectors.toList());
223 private List<GraphVertex> findModelVerticesByCriteria(final Map<GraphPropertyEnum, Object> propertyCriteria) {
224 final Either<List<GraphVertex>, JanusGraphOperationStatus> result = janusGraphDao.getByCriteria(VertexTypeEnum.MODEL, propertyCriteria);
225 if (result.isRight()) {
226 final var janusGraphOperationStatus = result.right().value();
227 if (janusGraphOperationStatus == JanusGraphOperationStatus.NOT_FOUND) {
228 return Collections.emptyList();
230 final var operationException = ModelOperationExceptionSupplier.failedToRetrieveModels(janusGraphOperationStatus).get();
231 log.error(EcompLoggerErrorCode.DATA_ERROR, this.getClass().getName(), operationException.getMessage());
232 throw operationException;
234 return result.left().value();
237 private Model convertToModel(final GraphVertex modelGraphVertex) {
238 final String modelName = (String) modelGraphVertex.getMetadataProperty(GraphPropertyEnum.NAME);
239 final String modelTypeProperty = (String) modelGraphVertex.getMetadataProperty(GraphPropertyEnum.MODEL_TYPE);
240 final ModelTypeEnum modelType = StringUtils.isEmpty(modelTypeProperty) ? ModelTypeEnum.NORMATIVE : ModelTypeEnum.findByValue(modelTypeProperty).get();
242 final Either<ImmutablePair<ModelData, GraphEdge>, JanusGraphOperationStatus> parentNode =
243 janusGraphGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Model), UniqueIdBuilder.buildModelUid(modelName),
244 GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Model, ModelData.class);
245 log.debug("After retrieving DERIVED_FROM node of {}. status is {}", modelName, parentNode);
246 if (parentNode.isRight()) {
247 final JanusGraphOperationStatus janusGraphOperationStatus = parentNode.right().value();
248 if (janusGraphOperationStatus != JanusGraphOperationStatus.NOT_FOUND) {
249 final var operationException = ModelOperationExceptionSupplier.failedToRetrieveModels(janusGraphOperationStatus).get();
250 log.error(EcompLoggerErrorCode.DATA_ERROR, this.getClass().getName(), operationException.getMessage());
251 throw operationException;
253 return new Model((String) modelGraphVertex.getMetadataProperty(GraphPropertyEnum.NAME), modelType);
255 final ModelData parentModel = parentNode.left().value().getKey();
256 return new Model((String) modelGraphVertex.getMetadataProperty(GraphPropertyEnum.NAME), parentModel.getName(), modelType);
260 public void addTypesToDefaultImports(final String typesYaml, final String modelName) {
261 final List<ToscaImportByModel> allSchemaImportsByModel = toscaModelImportCassandraDao.findAllByModel(modelName);
262 final Optional<ToscaImportByModel> additionalTypeDefinitionsOptional = allSchemaImportsByModel.stream()
263 .filter(t -> ADDITIONAL_TYPE_DEFINITIONS.equals(t.getFullPath())).findAny();
264 final ToscaImportByModel toscaImportByModelAdditionalTypeDefinitions;
265 final List<ToscaImportByModel> schemaImportsByModel;
266 if (additionalTypeDefinitionsOptional.isPresent()) {
267 toscaImportByModelAdditionalTypeDefinitions = additionalTypeDefinitionsOptional.get();
268 schemaImportsByModel = allSchemaImportsByModel.stream()
269 .filter(toscaImportByModel -> !ADDITIONAL_TYPE_DEFINITIONS.equals(toscaImportByModel.getFullPath()))
270 .collect(Collectors.toList());
272 toscaImportByModelAdditionalTypeDefinitions = new ToscaImportByModel();
273 toscaImportByModelAdditionalTypeDefinitions.setModelId(modelName);
274 toscaImportByModelAdditionalTypeDefinitions.setFullPath(ADDITIONAL_TYPE_DEFINITIONS);
275 toscaImportByModelAdditionalTypeDefinitions.setContent(typesYaml);
276 schemaImportsByModel = new ArrayList<>(allSchemaImportsByModel);
279 final List<ToscaImportByModel> toscaImportByModels = removeExistingDefaultImports(typesYaml, schemaImportsByModel);
281 final Map<String, Object> originalContent = (Map<String, Object>) new Yaml().load(toscaImportByModelAdditionalTypeDefinitions.getContent());
282 toscaImportByModelAdditionalTypeDefinitions.setContent(buildAdditionalTypeDefinitionsContent(typesYaml, originalContent).toString());
283 toscaImportByModels.add(toscaImportByModelAdditionalTypeDefinitions);
285 toscaModelImportCassandraDao.importOnly(modelName, toscaImportByModels);
288 private List<ToscaImportByModel> removeExistingDefaultImports(final String typesYaml, final List<ToscaImportByModel> schemaImportsByModel) {
289 final List<ToscaImportByModel> toscaImportByModels = new ArrayList<>();
290 schemaImportsByModel.forEach(toscaImportByModel -> {
291 final ToscaImportByModel toscaImportByModelNew = new ToscaImportByModel();
292 toscaImportByModelNew.setModelId(toscaImportByModel.getModelId());
293 toscaImportByModelNew.setFullPath(toscaImportByModel.getFullPath());
295 final Map<String, Object> existingImportYamlMap = (Map<String, Object>) new Yaml().load(toscaImportByModel.getContent());
297 ((Map<String, Object>) new Yaml().load(typesYaml)).keySet().forEach(existingImportYamlMap::remove);
299 final StringBuilder stringBuilder = new StringBuilder();
300 existingImportYamlMap.forEach((key, value) -> {
301 final Map<Object, Object> hashMap = new HashMap<>();
302 hashMap.put(key, value);
303 stringBuilder.append("\n").append(new YamlUtil().objectToYaml(hashMap));
306 toscaImportByModelNew.setContent(stringBuilder.toString());
307 toscaImportByModels.add(toscaImportByModelNew);
309 return toscaImportByModels;
312 private StringBuilder buildAdditionalTypeDefinitionsContent(final String typesYaml, final Map<String, Object> originalContent) {
313 final var stringBuilder = new StringBuilder();
315 final Map<String, Object> typesYamlMap = (Map<String, Object>) new Yaml().load(typesYaml);
316 final Set<String> typeYmlKeySet = typesYamlMap.keySet();
318 originalContent.forEach((key, value) -> {
319 final Map<Object, Object> hashMap = new HashMap<>();
320 if (typeYmlKeySet.contains(key)) {
321 hashMap.put(key, typesYamlMap.get(key));
323 hashMap.put(key, value);
325 final String newContent = new YamlUtil().objectToYaml(hashMap);
326 stringBuilder.append("\n").append(newContent);
328 return stringBuilder;