c33b2dd3b419ba1f92b96f54d01632c16ddc96d3
[sdc.git] / openecomp-be / lib / openecomp-sdc-vendor-software-product-lib / openecomp-sdc-vendor-software-product-core / src / main / java / org / openecomp / sdc / vendorsoftwareproduct / services / impl / composition / CompositionEntityDataManagerImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.openecomp.sdc.vendorsoftwareproduct.services.impl.composition;
22
23 import org.apache.commons.collections4.CollectionUtils;
24 import org.apache.commons.collections4.MapUtils;
25 import org.openecomp.core.utilities.json.JsonSchemaDataGenerator;
26 import org.openecomp.core.utilities.json.JsonUtil;
27 import org.openecomp.sdc.common.errors.CoreException;
28 import org.openecomp.sdc.common.errors.ErrorCategory;
29 import org.openecomp.sdc.common.errors.ErrorCode;
30 import org.openecomp.sdc.logging.api.Logger;
31 import org.openecomp.sdc.logging.api.LoggerFactory;
32 import org.openecomp.sdc.logging.context.impl.MdcDataDebugMessage;
33 import org.openecomp.sdc.vendorsoftwareproduct.dao.ComponentDao;
34 import org.openecomp.sdc.vendorsoftwareproduct.dao.NetworkDao;
35 import org.openecomp.sdc.vendorsoftwareproduct.dao.NicDao;
36 import org.openecomp.sdc.vendorsoftwareproduct.dao.VendorSoftwareProductInfoDao;
37 import org.openecomp.sdc.vendorsoftwareproduct.dao.type.ComponentEntity;
38 import org.openecomp.sdc.vendorsoftwareproduct.dao.type.CompositionEntity;
39 import org.openecomp.sdc.vendorsoftwareproduct.dao.type.NetworkEntity;
40 import org.openecomp.sdc.vendorsoftwareproduct.dao.type.NicEntity;
41 import org.openecomp.sdc.vendorsoftwareproduct.dao.type.VspDetails;
42 import org.openecomp.sdc.vendorsoftwareproduct.dao.type.VspQuestionnaireEntity;
43 import org.openecomp.sdc.vendorsoftwareproduct.services.composition.CompositionEntityDataManager;
44 import org.openecomp.sdc.vendorsoftwareproduct.services.schemagenerator.SchemaGenerator;
45 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Component;
46 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.ComponentData;
47 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.CompositionData;
48 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.CompositionEntityId;
49 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.CompositionEntityType;
50 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.CompositionEntityValidationData;
51 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Network;
52 import org.openecomp.sdc.vendorsoftwareproduct.types.composition.Nic;
53 import org.openecomp.sdc.vendorsoftwareproduct.types.schemagenerator.SchemaTemplateContext;
54 import org.openecomp.sdc.vendorsoftwareproduct.types.schemagenerator.SchemaTemplateInput;
55 import org.openecomp.sdc.versioning.dao.types.Version;
56
57 import java.util.ArrayList;
58 import java.util.Collection;
59 import java.util.HashMap;
60 import java.util.HashSet;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Objects;
64 import java.util.Set;
65
66 public class CompositionEntityDataManagerImpl implements CompositionEntityDataManager {
67
68   private static final String COMPOSITION_ENTITY_DATA_MANAGER_ERR =
69       "COMPOSITION_ENTITY_DATA_MANAGER_ERR";
70   private static final String COMPOSITION_ENTITY_DATA_MANAGER_ERR_MSG =
71       "Invalid input: %s may not be null";
72
73   private static final Logger logger =
74       LoggerFactory.getLogger(CompositionEntityDataManagerImpl.class);
75   private static MdcDataDebugMessage mdcDataDebugMessage = new MdcDataDebugMessage();
76
77   private Map<CompositionEntityId, CompositionEntityData> entities = new HashMap<>();
78   private Map<CompositionEntityType, String> nonDynamicSchemas = new HashMap<>();
79   private List<CompositionEntityValidationData> roots = new ArrayList<>();
80
81   private VendorSoftwareProductInfoDao vspInfoDao;
82   private ComponentDao componentDao;
83   private NicDao nicDao;
84   private NetworkDao networkDao;
85
86   public CompositionEntityDataManagerImpl(VendorSoftwareProductInfoDao vspInfoDao,
87                                           ComponentDao componentDao,
88                                           NicDao nicDao, NetworkDao networkDao) {
89     this.vspInfoDao = vspInfoDao;
90     this.componentDao = componentDao;
91     this.nicDao = nicDao;
92     this.networkDao = networkDao;
93   }
94
95   /**
96    * Validate entity composition entity validation data.
97    *
98    * @param entity                the entity
99    * @param schemaTemplateContext the schema template context
100    * @param schemaTemplateInput   the schema template input
101    * @return the composition entity validation data
102    */
103   @Override
104   public CompositionEntityValidationData validateEntity(CompositionEntity entity,
105                                                         SchemaTemplateContext schemaTemplateContext,
106                                                         SchemaTemplateInput schemaTemplateInput) {
107     mdcDataDebugMessage.debugEntryMessage(null);
108
109     if (entity == null) {
110       throw new CoreException(
111           new ErrorCode.ErrorCodeBuilder().withCategory(ErrorCategory.APPLICATION)
112               .withId(COMPOSITION_ENTITY_DATA_MANAGER_ERR).withMessage(
113               String.format(COMPOSITION_ENTITY_DATA_MANAGER_ERR_MSG, "composition entity"))
114               .build());
115     }
116     if (schemaTemplateContext == null) {
117       throw new CoreException(
118           new ErrorCode.ErrorCodeBuilder().withCategory(ErrorCategory.APPLICATION)
119               .withId(COMPOSITION_ENTITY_DATA_MANAGER_ERR).withMessage(
120               String.format(COMPOSITION_ENTITY_DATA_MANAGER_ERR_MSG, "schema template context"))
121               .build());
122     }
123
124     CompositionEntityValidationData validationData =
125         new CompositionEntityValidationData(entity.getType(), entity.getId());
126     String json =
127         schemaTemplateContext == SchemaTemplateContext.composition ? entity.getCompositionData()
128             : entity.getQuestionnaireData();
129     validationData.setErrors(JsonUtil.validate(
130         json == null ? JsonUtil.object2Json(new Object()) : json,
131         generateSchema(schemaTemplateContext, entity.getType(), schemaTemplateInput)));
132
133     mdcDataDebugMessage.debugExitMessage(null);
134     return validationData;
135   }
136
137   /**
138    * Add entity.
139    *
140    * @param entity              the entity
141    * @param schemaTemplateInput the schema template input
142    */
143   @Override
144   public void addEntity(CompositionEntity entity, SchemaTemplateInput schemaTemplateInput) {
145     if (entity == null) {
146       throw new CoreException(
147           new ErrorCode.ErrorCodeBuilder().withCategory(ErrorCategory.APPLICATION)
148               .withId(COMPOSITION_ENTITY_DATA_MANAGER_ERR).withMessage(
149               String.format(COMPOSITION_ENTITY_DATA_MANAGER_ERR_MSG, "composition entity"))
150               .build());
151     }
152     entities.put(entity.getCompositionEntityId(),
153         new CompositionEntityData(entity, schemaTemplateInput));
154   }
155
156   /**
157    * Validate entities questionnaire map.
158    *
159    * @return the map
160    */
161   @Override
162   public Map<CompositionEntityId, Collection<String>> validateEntitiesQuestionnaire() {
163     mdcDataDebugMessage.debugEntryMessage(null);
164
165     Map<CompositionEntityId, Collection<String>> errorsByEntityId = new HashMap<>();
166     entities.entrySet().forEach(entry -> {
167       Collection<String> errors = validateQuestionnaire(entry.getValue());
168       if (errors != null) {
169         errorsByEntityId.put(entry.getKey(), errors);
170       }
171     });
172
173     mdcDataDebugMessage.debugExitMessage(null);
174     return errorsByEntityId;
175   }
176
177   /**
178    * Build trees.
179    */
180   @Override
181   public void buildTrees() {
182     Map<CompositionEntityId, CompositionEntityValidationData> entitiesValidationData =
183         new HashMap<>();
184     entities.entrySet().forEach(
185         entry -> addValidationDataEntity(entitiesValidationData, entry.getKey(),
186             entry.getValue().entity));
187   }
188
189   public Collection<CompositionEntityValidationData> getTrees() {
190     return roots;
191   }
192
193   @Override
194   public void saveCompositionData(String vspId, Version version, CompositionData compositionData) {
195     mdcDataDebugMessage.debugEntryMessage(null);
196
197     if (Objects.isNull(compositionData)) {
198       return;
199     }
200
201     Map<String, String> networkIdByName = saveNetworks(vspId, version, compositionData);
202     saveComponents(vspId, version, compositionData, networkIdByName);
203
204     mdcDataDebugMessage.debugExitMessage(null);
205   }
206
207   @Override
208   public Set<CompositionEntityValidationData> getAllErrorsByVsp(String vspId) {
209     CompositionEntityValidationData matchVsp = null;
210     Set<CompositionEntityValidationData> entitiesWithErrors = new HashSet<>();
211     for (CompositionEntityValidationData root : roots) {
212       if (root.getEntityId().equals(vspId)) {
213         matchVsp = root;
214         break;
215       }
216     }
217
218     getEntityListWithErrors(matchVsp, entitiesWithErrors);
219     if (CollectionUtils.isNotEmpty(entitiesWithErrors)) {
220       updateValidationCompositionEntityName(entitiesWithErrors);
221       return entitiesWithErrors;
222     }
223
224     return null;
225   }
226
227   private boolean isThereErrorsInSubTree(CompositionEntityValidationData entity) {
228     if (Objects.isNull(entity)) {
229       return false;
230     }
231
232     if (CollectionUtils.isNotEmpty(entity.getErrors())) {
233       return true;
234     }
235
236     Collection<CompositionEntityValidationData> subEntitiesValidationData =
237         entity.getSubEntitiesValidationData();
238     return !CollectionUtils.isEmpty(subEntitiesValidationData) &&
239         checkForErrorsInChildren(subEntitiesValidationData);
240
241   }
242
243   private boolean checkForErrorsInChildren(
244       Collection<CompositionEntityValidationData> subEntitiesValidationData) {
245     boolean result = false;
246     for (CompositionEntityValidationData subEntity : subEntitiesValidationData) {
247       if (CollectionUtils.isNotEmpty(subEntity.getErrors())) {
248         return true;
249       }
250
251       result = result || isThereErrorsInSubTree(subEntity);
252       if (result) {
253         return true;
254       }
255     }
256     return false;
257   }
258
259   public void saveComponents(String vspId, Version version, CompositionData compositionData,
260                              Map<String, String> networkIdByName) {
261
262
263     mdcDataDebugMessage.debugEntryMessage(null);
264
265     if (CollectionUtils.isNotEmpty(compositionData.getComponents())) {
266       for (Component component : compositionData.getComponents()) {
267         ComponentEntity componentEntity = new ComponentEntity(vspId, version, null);
268         componentEntity.setComponentCompositionData(component.getData());
269
270         String componentId = createComponent(componentEntity).getId();
271
272         saveNicsByComponent(vspId, version, networkIdByName, component, componentId);
273       }
274     }
275
276     mdcDataDebugMessage.debugExitMessage(null);
277   }
278
279   public void saveNicsByComponent(String vspId, Version version,
280                                   Map<String, String> networkIdByName, Component component,
281                                   String componentId) {
282     if (CollectionUtils.isNotEmpty(component.getNics())) {
283       for (Nic nic : component.getNics()) {
284         if (nic.getNetworkName() != null && MapUtils.isNotEmpty(networkIdByName)) {
285           nic.setNetworkId(networkIdByName.get(nic.getNetworkName()));
286         }
287         nic.setNetworkName(null);
288
289         NicEntity nicEntity = new NicEntity(vspId, version, componentId, null);
290         nicEntity.setNicCompositionData(nic);
291         createNic(nicEntity);
292       }
293     }
294   }
295
296   public Map<String, String> saveNetworks(String vspId, Version version,
297                                           CompositionData compositionData) {
298     mdcDataDebugMessage.debugEntryMessage(null);
299
300     Map<String, String> networkIdByName = new HashMap<>();
301     if (CollectionUtils.isNotEmpty(compositionData.getNetworks())) {
302       for (Network network : compositionData.getNetworks()) {
303
304         NetworkEntity networkEntity = new NetworkEntity(vspId, version, null);
305         networkEntity.setNetworkCompositionData(network);
306
307         if (network.getName() != null) {
308           networkIdByName.put(network.getName(), createNetwork(networkEntity).getId());
309         }
310       }
311     }
312
313     mdcDataDebugMessage.debugExitMessage(null);
314     return networkIdByName;
315   }
316
317   @Override
318   public NetworkEntity createNetwork(NetworkEntity network) {
319     mdcDataDebugMessage.debugEntryMessage(null);
320
321     //network.setId(CommonMethods.nextUuId()); will be set by the dao
322     networkDao.create(network);
323     mdcDataDebugMessage.debugExitMessage(null);
324     return network;
325   }
326
327   @Override
328   public ComponentEntity createComponent(ComponentEntity component) {
329     mdcDataDebugMessage.debugEntryMessage(null);
330
331     //component.setId(CommonMethods.nextUuId()); will be set by the dao
332     component.setQuestionnaireData(
333         new JsonSchemaDataGenerator(
334             generateSchema(SchemaTemplateContext.questionnaire, CompositionEntityType.component,
335                 null))
336             .generateData());
337
338     componentDao.create(component);
339
340     mdcDataDebugMessage.debugExitMessage(null);
341     return component;
342   }
343
344   @Override
345   public NicEntity createNic(NicEntity nic) {
346     mdcDataDebugMessage.debugEntryMessage(null);
347
348     //nic.setId(CommonMethods.nextUuId()); will be set by the dao
349     nic.setQuestionnaireData(
350         new JsonSchemaDataGenerator(
351             generateSchema(SchemaTemplateContext.questionnaire, CompositionEntityType.nic, null))
352             .generateData());
353
354     nicDao.create(nic);
355
356     mdcDataDebugMessage.debugExitMessage(null);
357     return nic;
358   }
359
360
361   public void addErrorsToTrees(Map<CompositionEntityId, Collection<String>> errors) {
362     roots.forEach(root -> addErrorsToTree(root, null, errors));
363   }
364
365   /* *
366   * get a flat list of all questionnaire entities that have validation errors
367   * */
368   public Set<CompositionEntityValidationData> getEntityListWithErrors() {
369     mdcDataDebugMessage.debugEntryMessage(null);
370     Set<CompositionEntityValidationData> treeAsList = new HashSet<>();
371
372     for (CompositionEntityValidationData entity : roots) {
373       if (CollectionUtils.isNotEmpty(entity.getErrors())) {
374         addNodeWithErrors(entity, treeAsList);
375       }
376       getEntityListWithErrors(entity, treeAsList);
377     }
378
379     updateValidationCompositionEntityName(treeAsList);
380
381     mdcDataDebugMessage.debugExitMessage(null);
382     return treeAsList;
383   }
384
385   public void getEntityListWithErrors(CompositionEntityValidationData entity,
386                                       Set<CompositionEntityValidationData> compositionSet) {
387     Collection<CompositionEntityValidationData> childNodes =
388         entity.getSubEntitiesValidationData();
389
390     if (CollectionUtils.isEmpty(childNodes)) {
391       return;
392     }
393
394     for (CompositionEntityValidationData child : childNodes) {
395       if (CollectionUtils.isNotEmpty(child.getErrors())) {
396         addNodeWithErrors(child, compositionSet);
397       }
398       getEntityListWithErrors(child, compositionSet);
399     }
400   }
401
402
403   public void addNodeWithErrors(CompositionEntityValidationData node,
404                                 Set<CompositionEntityValidationData> entitiesWithErrors) {
405     CompositionEntityValidationData compositionNodeToAdd = new CompositionEntityValidationData(node
406         .getEntityType(), node.getEntityId());
407     compositionNodeToAdd.setErrors(node.getErrors());
408     compositionNodeToAdd.setSubEntitiesValidationData(null);
409
410     entitiesWithErrors.add(compositionNodeToAdd);
411   }
412
413   public void removeNodesWithoutErrors() {
414     roots.forEach(root -> removeNodesWithoutErrors(root, null));
415   }
416
417
418   private CompositionEntityData getCompositionEntityDataById(CompositionEntityValidationData
419                                                                  entity) {
420     for (Map.Entry<CompositionEntityId, CompositionEntityData> entityEntry : entities
421         .entrySet()) {
422       if (entityEntry.getKey().getId().equals(entity.getEntityId())) {
423         return entityEntry.getValue();
424       }
425     }
426     return null;
427   }
428
429
430   private void updateValidationCompositionEntityName(Set<CompositionEntityValidationData>
431                                                          compositionSet) {
432     for (CompositionEntityValidationData entity : compositionSet) {
433       String compositionData = getCompositionDataAsString(entity);
434       if (entity.getEntityType().equals(CompositionEntityType.vsp) ||
435           Objects.nonNull(compositionData)) {
436         entity.setEntityName(getEntityNameByEntityType(compositionData, entity));
437       }
438     }
439   }
440
441   private String getCompositionDataAsString(CompositionEntityValidationData entity) {
442     CompositionEntityData compositionEntityData = getCompositionEntityDataById(entity);
443     return compositionEntityData == null ? null : compositionEntityData.entity.getCompositionData();
444   }
445
446
447   private String getEntityNameByEntityType(String compositionData,
448                                            CompositionEntityValidationData entity) {
449     switch (entity.getEntityType()) {
450       case component:
451         ComponentData component = JsonUtil.json2Object(compositionData, ComponentData.class);
452         return component.getDisplayName();
453
454       case nic:
455         Nic nic = JsonUtil.json2Object(compositionData, Nic.class);
456         return nic.getName();
457
458       case network:
459         Network network = JsonUtil.json2Object(compositionData, Network.class);
460         return network.getName();
461
462       case vsp:
463         CompositionEntityData vspEntity = getCompositionEntityDataById(entity);
464         VspQuestionnaireEntity vspQuestionnaireEntity = (VspQuestionnaireEntity) vspEntity.entity;
465         VspDetails vspDetails =
466             vspInfoDao.get(new VspDetails(vspQuestionnaireEntity.getId(),
467                 vspQuestionnaireEntity.getVersion()));
468         return vspDetails.getName();
469     }
470
471     return null;
472   }
473
474   private void removeNodesWithoutErrors(CompositionEntityValidationData node,
475                                         CompositionEntityValidationData parent) {
476
477     if (Objects.isNull(node)) {
478       return;
479     }
480
481     if (hasChildren(node)) {
482       Collection<CompositionEntityValidationData> subNodes =
483           new ArrayList<>(node.getSubEntitiesValidationData());
484       subNodes.forEach(subNode -> removeNodesWithoutErrors(subNode, node));
485       node.setSubEntitiesValidationData(subNodes);
486
487       if (canNodeGetRemovedFromValidationDataTree(node)) {
488         removeNodeFromChildren(parent, node);
489       }
490     } else if (canNodeGetRemovedFromValidationDataTree(node)) {
491       removeNodeFromChildren(parent, node);
492     }
493   }
494
495   private void removeNodeFromChildren(CompositionEntityValidationData parent,
496                                       CompositionEntityValidationData childToRemove) {
497     if (!Objects.isNull(parent)) {
498       parent.getSubEntitiesValidationData().remove(childToRemove);
499     }
500   }
501
502   private boolean hasChildren(CompositionEntityValidationData node) {
503     return !CollectionUtils.isEmpty(node.getSubEntitiesValidationData());
504   }
505
506   private boolean canNodeGetRemovedFromValidationDataTree(CompositionEntityValidationData node) {
507     return !hasChildren(node) && CollectionUtils.isEmpty(node.getErrors());
508   }
509
510
511   private void addValidationDataEntity(
512       Map<CompositionEntityId, CompositionEntityValidationData> entitiesValidationData,
513       CompositionEntityId entityId, CompositionEntity entity) {
514     if (entitiesValidationData.containsKey(entityId)) {
515       return;
516     }
517
518     CompositionEntityValidationData validationData =
519         new CompositionEntityValidationData(entity.getType(), entity.getId());
520     entitiesValidationData.put(entityId, validationData);
521
522     CompositionEntityId parentEntityId = entityId.getParentId();
523     if (parentEntityId == null) {
524       roots.add(validationData);
525     } else {
526       CompositionEntityData parentEntity = entities.get(parentEntityId);
527       if (parentEntity == null) {
528         roots.add(validationData);
529       } else {
530         addValidationDataEntity(entitiesValidationData, parentEntityId, parentEntity.entity);
531         entitiesValidationData.get(parentEntityId).addSubEntityValidationData(validationData);
532       }
533     }
534   }
535
536   private void addErrorsToTree(CompositionEntityValidationData node,
537                                CompositionEntityId parentNodeId,
538                                Map<CompositionEntityId, Collection<String>> errors) {
539     if (node == null) {
540       return;
541     }
542     CompositionEntityId nodeId = new CompositionEntityId(node.getEntityId(), parentNodeId);
543     node.setErrors(errors.get(nodeId));
544
545     if (node.getSubEntitiesValidationData() != null) {
546       node.getSubEntitiesValidationData()
547           .forEach(subNode -> addErrorsToTree(subNode, nodeId, errors));
548     }
549   }
550
551   private Collection<String> validateQuestionnaire(CompositionEntityData compositionEntityData) {
552     logger.debug(String.format("validateQuestionnaire start:  " +
553             "[entity.type]=%s, [entity.id]=%s, [entity.questionnaireString]=%s",
554         compositionEntityData.entity.getType().name(),
555         compositionEntityData.entity.getCompositionEntityId().toString(),
556         compositionEntityData.entity.getQuestionnaireData()));
557
558     return JsonUtil.validate(
559         compositionEntityData.entity.getQuestionnaireData() == null
560             ? JsonUtil.object2Json(new Object())
561             : compositionEntityData.entity.getQuestionnaireData(),
562         getSchema(compositionEntityData.entity.getType(), SchemaTemplateContext.questionnaire,
563             compositionEntityData.schemaTemplateInput));
564   }
565
566   private String getSchema(CompositionEntityType compositionEntityType,
567                            SchemaTemplateContext schemaTemplateContext,
568                            SchemaTemplateInput schemaTemplateInput) {
569     return schemaTemplateInput == null
570         ? nonDynamicSchemas.computeIfAbsent(compositionEntityType,
571         k -> generateSchema(schemaTemplateContext, compositionEntityType, null))
572         : generateSchema(schemaTemplateContext, compositionEntityType, schemaTemplateInput);
573   }
574
575   private static class CompositionEntityData {
576     private CompositionEntity entity;
577     private SchemaTemplateInput schemaTemplateInput;
578
579     CompositionEntityData(CompositionEntity entity, SchemaTemplateInput schemaTemplateInput) {
580       this.entity = entity;
581       this.schemaTemplateInput = schemaTemplateInput;
582     }
583
584   }
585
586   // todo - make SchemaGenerator non static and mock it in UT instead of mocking this method (and
587   // make the method private
588
589   protected String generateSchema(SchemaTemplateContext schemaTemplateContext,
590                                   CompositionEntityType compositionEntityType,
591                                   SchemaTemplateInput schemaTemplateInput) {
592     return SchemaGenerator
593         .generate(schemaTemplateContext, compositionEntityType, schemaTemplateInput);
594   }
595 }