Update license files, sonar plugin and fix tests
[aai/aai-common.git] / aai-core / src / main / java / org / openecomp / aai / dbgraphgen / DbSearchWithTags.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * org.openecomp.aai
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.aai.dbgraphgen;
22
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.LinkedHashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29
30 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
31 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
32 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
33 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
34 import org.apache.tinkerpop.gremlin.structure.Element;
35 import org.apache.tinkerpop.gremlin.structure.Vertex;
36
37 import org.openecomp.aai.db.props.AAIProperties;
38 import org.openecomp.aai.exceptions.AAIException;
39 import org.openecomp.aai.introspection.Loader;
40 import org.openecomp.aai.serialization.db.DBSerializer;
41 import org.openecomp.aai.serialization.engines.TransactionalGraphEngine;
42 import org.openecomp.aai.util.AAIConfig;
43 import com.att.eelf.configuration.EELFLogger;
44 import com.att.eelf.configuration.EELFManager;
45
46
47
48 /**
49  * Database-level Search-Utility class that uses edge-tags to help it navigate the graph.   
50  */
51 public class DbSearchWithTags{
52
53         private EELFLogger LOGGER = EELFManager.getInstance().getLogger(DbSearchWithTags.class);
54
55         private TransactionalGraphEngine engine;
56         
57         protected DbSearchWithTags() {
58                 
59         }
60         public DbSearchWithTags(Loader loader, TransactionalGraphEngine engine, DBSerializer serializer) {
61                 this.engine = engine;
62         }
63
64         /**
65          * Run edge tag query.
66          *
67          * @param transId the trans id
68          * @param fromAppId the from app id
69          * @param edgeTag the edge tag
70          * @param searchRootType - root collection point when doing the search
71          * @param displayRootType - if they want more data than would be included using the searchRootType, this
72          *                            lets them specify it.
73          * @param searchDirectionStr the search direction str
74          * @param initialFilterHash the initial filter hash
75          * @param secondaryFilterHash the secondary filter hash
76          * @param pruneLevel   either "none", "searchLevel" or "displayLevel".
77          * @param retNodeType    could be either "all" for the full map, or a single nodeType
78          * @param trimList     list of nodes to "stop" at when collecting data
79          * @return List<resultSet>
80          * @throws AAIException the AAI exception
81          */
82           public Tree<Vertex> runEdgeTagQuery( String transId, String fromAppId,
83                         String edgeTag, 
84                         String searchRootType, 
85                         Map<String,Object> initialFilterHash, 
86                         Map<String,Object> secondaryFilterHash) 
87                             throws AAIException{
88                 
89                 final String tag = edgeTag;
90                 final String reverseTag = edgeTag + "-REV";
91                 //max levels is not used at this time, but may be used again
92                 int maxLevels = 50; // default value
93                 String maxString = AAIConfig.get("aai.edgeTag.proc.max.levels");
94                 if( maxString != null &&  !maxString.equals("") ){
95                         try {
96                                 int maxVal = Integer.parseInt(maxString);
97                                 maxLevels = maxVal;
98                         }
99                         catch ( Exception nfe ){
100                                 // Don't worry, we will leave "maxLevels" set to the default value it was initialized with 
101                         }
102                 }
103           
104                 // First, we need to use the intialFilter plus the edgeTag to identify a set of search-root-nodes
105                 HashMap <String, Vertex> searchRootHash = identifyTopNodeSet( transId, fromAppId,
106                                 edgeTag, searchRootType, initialFilterHash, maxLevels );
107                 
108                 
109                 Set<String> keySet = searchRootHash.keySet();
110                 Iterator<String> itr = keySet.iterator();
111                 String[] arrayOfVertices = new String[keySet.size()];
112                 int i = 0;
113                 while (itr.hasNext()) {
114                         arrayOfVertices[i] = itr.next();
115                         i++;
116                 }
117                 //start from all vertices provided
118                 //repeat checking the out edge for the tag and the in edge for reverse tag
119                 //emit all vertices not already seen and start again
120                 //return a tree structure of the vertices and edges touched by this traversal
121                 Tree<Element> resultTree = this.engine.asAdmin().getReadOnlyTraversalSource().V(arrayOfVertices)
122                                 .emit().repeat(__.union(__.outE().has(tag, true), __.inE().has(reverseTag, true)).otherV()).tree().next();
123                 
124                 //the resulting tree includes the edges because of our query, we'll need to remove them
125                 Tree<Vertex> sanitizedTree = removeUnwantedItems(resultTree);
126
127                 //if we have secondary filters then check each tree returned for matches
128                 if (!secondaryFilterHash.isEmpty()) {
129                         filterOutResultTrees(sanitizedTree, secondaryFilterHash);
130                 }
131                 return sanitizedTree;
132                 
133           }// End of runEdgeTagQuery()
134         
135         /**
136          * Identify top node set.
137          *
138          * @param transId the trans id
139          * @param fromAppId the from app id
140          * @param edgeTag the edge tag
141          * @param topNodeType the top node type
142          * @param initialFilterHash the initial filter hash
143          * @param maxLevels the max levels
144          * @return List<titanVertex>
145          * @throws AAIException the AAI exception
146          */
147           public HashMap<String, Vertex> identifyTopNodeSet( String transId, String fromAppId,
148                           String edgeTag, String topNodeType, Map<String,Object> initialFilterHash, int maxLevels )   
149                                           throws AAIException {
150                   
151                   final String tag = edgeTag;
152                   final String reverseTag = edgeTag + "-REV";
153                   HashMap <String, Vertex> topVertHash = new HashMap <>();
154                   
155                   // Given the filter, we want to select all the nodes of the type the filter tells us that have the
156                   //    property they gave us.
157                   // Then looping through those start points, we will look "up" and then "down" to find the set of target/top nodes.
158                  
159                   if( initialFilterHash == null || initialFilterHash.isEmpty() ){
160                           throw new AAIException("AAI_6118", " initialFilterHash is required for identifyInitialNodeSet() call. \n"); 
161                   }
162
163                   // NOTE: we're expecting the filter to have a format like this: "nodeType.parameterName:parameterValue"
164                   Iterator <?> it = initialFilterHash.entrySet().iterator();
165                   // -- DEBUG -- for now we only deal with ONE initial filter parameter
166                   //     it would be easy enough to deal with multiple parameters if they all
167                   //     applied to the same nodeType.
168                   String propNodeTypeDotName = "";
169                   String initNodeType = "";
170                   String initPropName = "";
171          
172                   String extraChecks = "";
173                   
174                   String propVal = "";
175                   if( it.hasNext() ){
176                           Map.Entry<?,?> propEntry = (Map.Entry<?,?>) it.next();
177                           propNodeTypeDotName = (propEntry.getKey()).toString();
178                           propVal = (propEntry.getValue()).toString();
179                   }
180                   
181                   GraphTraversalSource source = this.engine.asAdmin().getReadOnlyTraversalSource();
182                   GraphTraversal<Vertex, Vertex> g;
183                                   
184                   int periodLoc = propNodeTypeDotName.indexOf(".");
185                   if( periodLoc <= 0 ){
186                           throw new AAIException("AAI_6120", "Bad filter param key passed in: [" + propNodeTypeDotName + "].  Expected format = [nodeName.paramName]\n"); 
187                   }
188                   else {
189                           initNodeType = propNodeTypeDotName.substring(0,periodLoc);
190                           initPropName = propNodeTypeDotName.substring(periodLoc + 1);
191                           
192                           //there used to be logic here that would do something special for generic-vnf.vnf-name and vserver.vserver-name
193                           //it would attempt a search as they sent it, if nothing came back, try as all upper, try as all lower, then fail
194                           //here it checks whether something comes back or not, if not change the case and try again
195                           if( (initNodeType.equals("generic-vnf") && initPropName.equals("vnf-name"))
196                                           || (initNodeType.equals("vserver") && initPropName.equals("vserver-name")) ){
197                                   if (!this.checkKludgeCase(initNodeType, initPropName, propVal)) {
198                                           if (this.checkKludgeCase(initNodeType, initPropName, propVal.toUpperCase())) {
199                                                   propVal = propVal.toUpperCase();
200                                           } else {
201                                                   if (this.checkKludgeCase(initNodeType, initPropName, propVal)) {
202                                                           propVal = propVal.toLowerCase();
203                                                   }
204                                           }
205                                   }
206                           }     
207                           g = source.V().has(AAIProperties.NODE_TYPE, initNodeType).has(initPropName, propVal);
208                           
209                           //search all around bounded by our edge tag for start nodes that match our topNodeType
210                           if (!topNodeType.equals(initNodeType)) {
211                                 
212                                 g.union(__.<Vertex>start().until(__.has(AAIProperties.NODE_TYPE, topNodeType))
213                                                  .repeat(__.union(__.inE().has(reverseTag, true), __.outE().has(tag, true)).otherV()),
214                                                  __.<Vertex>start().until(__.has(AAIProperties.NODE_TYPE, topNodeType))
215                                                  .repeat(__.union(__.inE().has(tag, true), __.outE().has(reverseTag, true)).otherV())).dedup();
216                           }
217                           
218                           List<Vertex> results = g.toList();
219                           
220                           results.forEach(v -> {
221                                   topVertHash.put(v.id().toString(), v);
222                           });
223                   }
224                   if( topVertHash.isEmpty() ){
225                                 // No Vertex was found  - throw a not-found exception
226                                 throw new AAIException("AAI_6114", "No Node of type " + topNodeType + " found for properties: " + initialFilterHash.toString() + extraChecks);
227                   }
228                   else {
229                           return topVertHash;
230                   }
231                   
232           }// End identifyInitialNodeSet()
233           
234           /**
235            * This is a carryover from the previous version.
236            * We may be able to remove this.
237            * 
238            * @param nodeType
239            * @param propName
240            * @param propValue
241            * @return
242            */
243           private boolean checkKludgeCase(String nodeType, String propName, String propValue) {
244                   return this.engine.getQueryBuilder().getVerticesByIndexedProperty(AAIProperties.NODE_TYPE, nodeType).getVerticesByProperty(propName, propValue).hasNext();
245           }
246           
247         /**
248          * This method starts from the top of the tree object and checks each nested tree.
249          * If that tree does not contain a node (or nodes) that match the filterHash, remove it
250          * 
251          * @param tree
252          * @param filterHash
253          * @throws AAIException
254          */
255         private void filterOutResultTrees(Tree<Vertex> tree, Map<String,Object> filterHash) throws AAIException {
256                 Set<Vertex> topLevelKeys = new LinkedHashSet<>(tree.keySet());
257                 for (Vertex topLevel : topLevelKeys) {
258                         if (!this.checkVertexWithFilters(topLevel, filterHash)) {
259                                 if (!filterOutResultTreesHelper(tree.get(topLevel), filterHash)) {
260                                         //if we never found anything to satisfy our filter, remove the entire result tree
261                                         tree.remove(topLevel);
262                                 }
263                         }
264                 }
265         }
266         
267         /**
268          * Checks all vertices of a tree with the provided filterHash
269          * 
270          * @param tree
271          * @param filterHash
272          * @return
273          * @throws AAIException
274          */
275         private boolean filterOutResultTreesHelper(Tree<Vertex> tree, Map<String,Object> filterHash) throws AAIException {
276                 
277                 Set<Vertex> keys = tree.keySet();
278
279                 for (Vertex v : keys) {
280                         if (checkVertexWithFilters(v, filterHash)) {
281                                 return true;
282                         } else {
283                                 if (filterOutResultTreesHelper(tree.get(v), filterHash)) {
284                                         return true;
285                                 }
286                         }
287                 }
288                 
289                 return false;
290         }
291         /**
292          * Checks whether a vertex matches the filterHash provided
293          * 
294          * @param v
295          * @param filterHash
296          * @return
297          * @throws AAIException
298          */
299         private boolean checkVertexWithFilters(Vertex v, Map<String,Object> filterHash) throws AAIException {
300                 Iterator <?> it = filterHash.entrySet().iterator();
301
302                 while( it.hasNext() ){
303                           Map.Entry<?,?> filtEntry = (Map.Entry<?,?>) it.next();
304                           String propNodeTypeDotName = (filtEntry.getKey()).toString();
305                           String value = (filtEntry.getValue()).toString();
306                           
307                           int periodLoc = propNodeTypeDotName.indexOf(".");
308                           if( periodLoc <= 0 ){
309                                   throw new AAIException("AAI_6120", "Bad filter param key passed in: [" + propNodeTypeDotName + "].  Expected format = [nodeName.paramName]\n"); 
310                           }
311                           else {
312                                   String nodeType = propNodeTypeDotName.substring(0,periodLoc);
313                                   String propertyName = propNodeTypeDotName.substring(periodLoc + 1);
314                                   String nt = v.<String>property("aai-node-type").orElse(null);
315                                   if( nt.equals( nodeType ) ){
316                                           if( propertyName.equals("vertex-id") ){
317                                                   // vertex-id can't be gotten the same way as other properties
318                                                   String thisVtxId = v.id().toString();
319                                                   if( thisVtxId.equals(value) ){
320                                                           return true;
321                                                   }
322                                           }
323                                           else {
324                                                   Object thisValObj = v.property(propertyName).orElse(null);
325                                                   if( thisValObj != null ){
326                                                           String thisVal = thisValObj.toString();
327                                                           if( thisVal.equals(value) ){
328                                                                   return true;
329                                                           }
330                                                   }
331                                           }
332                                   }                               
333                           }
334                   }
335                 
336                  return false;
337         }
338         
339         /**
340          * Removes every other tree from the originalTree provided.
341          * It is designed to specifically handle removing unwanted edges from the originalTree
342          * @param originalTree
343          * @return
344          */
345         private Tree<Vertex> removeUnwantedItems(Tree<Element> originalTree) {
346                 
347                 Tree<Vertex> newTree = new Tree<>();
348                 Set<Element> keys = originalTree.keySet();
349                 for (Element element : keys) {
350                         newTree.put((Vertex)element, removeUnwantedItemsHelper(originalTree.get(element).getTreesAtDepth(2)));
351                 }
352
353                 return newTree;
354                 
355                 
356         }
357         
358         private Tree<Vertex> removeUnwantedItemsHelper(List<Tree<Element>> originalTrees) {
359                 Tree<Vertex> newTree = new Tree<>();
360                 for (Tree<Element> tree : originalTrees) {
361                         newTree.addTree(removeUnwantedItems(tree));
362                 }
363                 
364                 return newTree;
365         }
366 }