DCAE-D be initial commit
[sdc/dcae-d/dt-be-main.git] / dcaedt_be / src / main / java / org / onap / sdc / dcae / rule / editor / impl / RulesBusinessLogic.java
1 package org.onap.sdc.dcae.rule.editor.impl;
2
3 import com.google.gson.Gson;
4 import com.google.gson.GsonBuilder;
5 import org.apache.commons.lang3.StringUtils;
6 import org.onap.sdc.common.onaplog.OnapLoggerDebug;
7 import org.onap.sdc.common.onaplog.Enums.LogLevel;
8 import org.onap.sdc.dcae.composition.restmodels.ruleeditor.*;
9 import org.onap.sdc.dcae.errormng.ActionStatus;
10 import org.onap.sdc.dcae.errormng.ErrConfMgr;
11 import org.onap.sdc.dcae.errormng.ResponseFormat;
12 import org.onap.sdc.dcae.errormng.ServiceException;
13 import org.onap.sdc.dcae.rule.editor.translators.MappingRulesTranslator;
14 import org.onap.sdc.dcae.rule.editor.utils.EmptyStringTranslationSerializer;
15 import org.onap.sdc.dcae.rule.editor.validators.RuleValidator;
16 import org.springframework.stereotype.Component;
17 import org.springframework.util.CollectionUtils;
18
19 import java.util.*;
20 import java.util.function.BiFunction;
21 import java.util.function.Function;
22 import java.util.stream.Collectors;
23
24 @Component
25 public class RulesBusinessLogic {
26
27         protected OnapLoggerDebug debugLogger = OnapLoggerDebug.getInstance();
28         private RuleValidator ruleValidator = RuleValidator.getInstance();
29         private MappingRulesTranslator mappingRulesTranslator = MappingRulesTranslator.getInstance();
30         private static Gson gsonTranslator = new GsonBuilder().registerTypeAdapter(String.class, new EmptyStringTranslationSerializer()).enableComplexMapKeySerialization().create();
31
32         public List<ServiceException> validateRule(Rule rule) {
33                 List<ResponseFormat> errors = new ArrayList<>();
34                 if(ruleValidator.validate(rule, errors))
35                         detectAndResolveActionDependencies(rule, errors);
36                 return errors.stream().map(r -> r.getRequestError().getServiceException()).collect(Collectors.toList());
37         }
38
39         public List<ServiceException> validateRules(MappingRules rules) {
40                 List<ResponseFormat> errors = new ArrayList<>();
41                 detectAndResolveRuleDependencies(rules, errors);
42                 return errors.stream().map(r -> r.getRequestError().getServiceException()).collect(Collectors.toList());
43         }
44
45         public String translateRules(MappingRules rules, String entryPointPhase, String lastPhase, String runPhase) {
46                 debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Start translating mapping rules");
47                 return gsonTranslator.toJson(mappingRulesTranslator.translateToHpJson(rules, entryPointPhase, lastPhase, runPhase));
48         }
49
50         public boolean addOrEditRule(MappingRules rules, Rule rule) {
51                 // in case the rule id is passed but the rule doesn't exist on the mapping rule file:
52                 if(StringUtils.isNotBlank(rule.getUid()) && !rules.ruleExists(rule))
53                         return false;
54                 rules.addOrReplaceRule(rule);
55                 return true;
56         }
57
58         public Rule deleteRule(MappingRules rules, String ruleUid) {
59                 return rules.removeRule(ruleUid);
60         }
61
62         private <T> List<T> detectDependentItemsByDependencyDefinition(Collection<T> allItems, BiFunction<T, Collection<T>, Boolean> dependencyDefinition) {
63                 return allItems.stream().filter(i -> dependencyDefinition.apply(i, allItems)).collect(Collectors.toList());
64         }
65
66         //       if all dependencies are resolvable returns empty list
67         //       else returns list of non resolvable items (circular dependent items)
68         //       iterate through all dependentItems removing resolvable items each iteration.
69
70         private <T> List<T> detectCircularDependenciesByDependencyDefinition(List<T> dependentItems, BiFunction<T, Collection<T>, Boolean> dependencyDetector) {
71                 while(!CollectionUtils.isEmpty(dependentItems)) {
72                         List<T> resolvable = dependentItems.stream()
73                                         .filter(i -> !dependencyDetector.apply(i, dependentItems))
74                                         .collect(Collectors.toList());
75                         if(CollectionUtils.isEmpty(resolvable))
76                                 break;
77                         dependentItems.removeAll(resolvable);
78                 }
79                 return dependentItems;
80         }
81
82         private <T> List<T> reorderItemsByDependencyDefinition(Collection<T> allItems, BiFunction<T, T, Boolean> dependencyDetector) {
83                 List<T> ordered = new ArrayList<>(allItems);
84                 allItems.forEach(i -> {
85                         List<T> dependencies = allItems.stream().filter(o -> dependencyDetector.apply(i, o)).collect(Collectors.toList());
86                         dependencies.forEach(d -> {
87                                 if(ordered.indexOf(d) > ordered.indexOf(i)) {
88                                         ordered.remove(d);
89                                         ordered.add(ordered.indexOf(i), d);
90                                 }
91                         });
92                 });
93                 return ordered;
94         }
95
96         private void detectAndResolveActionDependencies(Rule rule, List<ResponseFormat> errors) {
97                 List<BaseAction> dependentActions = detectDependentItemsByDependencyDefinition(rule.getActions(), BaseAction::hasDependencies);
98                 if(!CollectionUtils.isEmpty(dependentActions)) {
99                         List<BaseAction> nonResolvable = detectCircularDependenciesByDependencyDefinition(dependentActions, BaseAction::hasDependencies);
100                         if (!CollectionUtils.isEmpty(nonResolvable)) {
101                                 errors.add(ErrConfMgr.INSTANCE.getResponseFormat(ActionStatus.ACTION_DEPENDENCY, null, nonResolvable.stream().map(BaseAction::getTarget).collect(Collectors.joining(", "))));
102                                 return;
103                         }
104                         List<BaseAction> actions = reorderItemsByDependencyDefinition(rule.getActions(), BaseAction::referencesTarget);
105                         rule.setActions(actions);
106                 }
107         }
108
109         // first identify dependent rules
110         // if no dependencies found return true
111         // if non resolvable dependencies found return false
112         // else reorder and return true
113
114         private void detectAndResolveRuleDependencies(MappingRules rules, List<ResponseFormat> errors) {
115                 List<Rule> dependentRules = detectDependentItemsByDependencyDefinition(rules.getRules().values(), Rule::referencesOtherRules);
116                 if(!CollectionUtils.isEmpty(dependentRules)) {
117                         List<Rule> nonResolvable = detectCircularDependenciesByDependencyDefinition(dependentRules, Rule::referencesOtherRules);
118                         if (!CollectionUtils.isEmpty(nonResolvable)) {
119                                 String nonResolvableRuleIds = nonResolvable.stream().map(Rule::getUid).collect(Collectors.joining(", "));
120                                 errors.add(ErrConfMgr.INSTANCE.getResponseFormat(ActionStatus.RULE_DEPENDENCY, null, nonResolvableRuleIds, extractDependentActionTargetsFromRules(nonResolvable)));
121                                 return;
122                         }
123                         reorderRulesByDependency(rules);
124                 }
125         }
126
127         private String extractDependentActionTargetsFromRules(List<Rule> dependentRules) {
128                 List<BaseAction> allActions = dependentRules.stream().map(Rule::getActions).flatMap(List::stream).collect(Collectors.toList());
129                 // option 1: circular dependency between actions
130                 List<BaseAction> nonResolvable = detectCircularDependenciesByDependencyDefinition(allActions, BaseAction::hasDependencies);
131                 if(CollectionUtils.isEmpty(nonResolvable))
132                         // option 2: circular dependency between rules - collect dependent actions and condition dependencies
133                         nonResolvable = dependentRules.stream()
134                                         .map(r -> r.findDependencies(dependentRules))
135                                         .flatMap(List::stream)
136                                         .collect(Collectors.toList());
137                 return nonResolvable.stream()
138                                 .map(BaseAction::getTarget)
139                                 .collect(Collectors.joining(", "));
140         }
141
142         private void reorderRulesByDependency(MappingRules rules) {
143                 List<Rule> ordered = reorderItemsByDependencyDefinition(rules.getRules().values(), Rule::referencesOtherRule);
144                 Map<String, Rule> rulesMap = ordered.stream().collect(Collectors.toMap(Rule::getUid, Function.identity(), (u, v) -> {
145                         throw new IllegalStateException(String.format("Duplicate key %s", u));
146                 }, LinkedHashMap::new));
147                 rules.setRules(rulesMap);
148         }
149 }