213ff5901a287e937d806e530dcd52f9bb3126f7
[aai/aai-common.git] / aai-schema-ingest / src / main / java / org / onap / aai / edges / EdgeIngestor.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-18 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.onap.aai.edges;
22
23 import com.att.eelf.configuration.EELFLogger;
24 import com.att.eelf.configuration.EELFManager;
25 import com.google.common.cache.CacheBuilder;
26 import com.google.common.cache.CacheLoader;
27 import com.google.common.cache.LoadingCache;
28 import com.google.common.collect.ArrayListMultimap;
29 import com.google.common.collect.Multimap;
30 import com.jayway.jsonpath.Criteria;
31 import com.jayway.jsonpath.DocumentContext;
32 import com.jayway.jsonpath.Filter;
33 import org.apache.tinkerpop.gremlin.structure.Direction;
34 import org.onap.aai.edges.enums.DirectionNotation;
35 import org.onap.aai.edges.enums.EdgeField;
36 import org.onap.aai.edges.enums.EdgeType;
37 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
38 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
39 import org.onap.aai.setup.SchemaVersion;
40 import org.onap.aai.setup.SchemaVersions;
41 import org.onap.aai.setup.Translator;
42 import org.springframework.beans.factory.annotation.Autowired;
43 import org.springframework.stereotype.Component;
44
45 import javax.annotation.PostConstruct;
46 import java.io.IOException;
47 import java.util.*;
48 import java.util.Map.Entry;
49 import java.util.concurrent.ExecutionException;
50 import java.util.stream.Collectors;
51
52 import static com.jayway.jsonpath.Criteria.where;
53 import static com.jayway.jsonpath.Filter.filter;
54
55 /**
56  * EdgeIngestor - ingests A&AI edge rule schema files per given config, serves that edge rule
57  *      information, including allowing various filters to extract particular rules.
58  */
59 @Component
60 public class EdgeIngestor {
61     private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(EdgeIngestor.class);
62     private Map<SchemaVersion, List<DocumentContext>> versionJsonFilesMap = new TreeMap<>();
63     private static final String READ_START = "$.rules.[?]";
64     private static final String READ_ALL_START = "$.rules.*";
65     private SchemaVersions schemaVersions;
66
67     Map<SchemaVersion, List<String>> filesToIngest;
68
69     private Set<String> multipleLabelKeys;
70
71     private LoadingCache<SchemaFilter,Multimap<String,EdgeRule>> cacheFilterStore;
72
73     private LoadingCache<String, String[]> cousinLabelStore;
74
75     private Set<Translator> translators;
76
77     @Autowired
78     public EdgeIngestor(Set<Translator> translatorSet) {
79         LOGGER.debug("Local Schema files will be fetched");
80         this.translators = translatorSet;
81     }
82
83     @PostConstruct
84     public void initialize() {
85
86        for (Translator translator : translators) {
87             try {
88                 LOGGER.debug("Processing the translator");
89                 translateAll(translator);
90
91             } catch (Exception e) {
92                 LOGGER.debug("Error while Processing the translator" + e.getMessage());
93                 continue;
94             }
95         }
96         if (versionJsonFilesMap.isEmpty() || schemaVersions==null ) {
97             throw new ExceptionInInitializerError("EdgeIngestor could not ingest edgerules");
98         }
99     }
100
101     public void translateAll(Translator translator) {
102         /*
103         Use SchemaVersions from the Translator
104          */
105         this.schemaVersions = translator.getSchemaVersions();
106         List<SchemaVersion> schemaVersionList = this.schemaVersions.getVersions();
107         List<String> jsonPayloads = null;
108         JsonIngestor ji = new JsonIngestor();
109         Map<SchemaVersion, List<String>> edgeRulesToIngest = new HashMap<>();         // Obtain a map of schema versions to a list of strings. One List per key
110
111         // Add to the map the JSON file per version.
112         for (SchemaVersion version : schemaVersionList) {
113             LOGGER.debug("Version being processed" + version);
114             // If the flag is set to not use the local files, obtain the Json from the service.
115             try {
116                 jsonPayloads = translator.getJsonPayload(version);     // need to change this - need to receive the json files.
117             } catch (IOException e) {
118                 LOGGER.info("Exception in retrieving the JSON Payload"+e.getMessage());
119             }
120             if (jsonPayloads == null || jsonPayloads.isEmpty()) {
121                 continue;
122             }
123             LOGGER.debug("Retrieved json from SchemaService");
124             edgeRulesToIngest.put(version, jsonPayloads);
125         }
126         versionJsonFilesMap = ji.ingestContent(edgeRulesToIngest);
127
128         this.cacheFilterStore = CacheBuilder.newBuilder()
129             .maximumSize(2000)
130             .build(
131                 new CacheLoader<SchemaFilter, Multimap<String, EdgeRule>>() {
132                     @Override
133                     public Multimap<String, EdgeRule> load(SchemaFilter key) {
134                         return extractRules(key);
135                     }
136                 }
137             );
138
139         this.cousinLabelStore = CacheBuilder.newBuilder()
140             .maximumSize(50)
141             .build(
142                 new CacheLoader<String, String[]>() {
143                     @Override
144                     public String[] load(String key) throws Exception {
145                         return retrieveCousinLabels(key);
146                     }
147                 }
148             );
149     }
150
151 //      //-----methods for getting rule info-----//
152 //
153     /**
154      * Gets list of all edge rules defined in the latest version's schema
155      *
156      * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules associated with those types
157      *          where the key takes the form of
158      *                  {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
159      *          no rules are found.
160      *          ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
161      *                  buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
162      *
163      *  This is alphabetical order to normalize the keys, as sometimes there will be multiple
164      *  rules for a pair of node types but the from/to value in the json is flipped for some of them.
165      * @throws EdgeRuleNotFoundException if none found
166      */
167     public Multimap<String, EdgeRule> getAllCurrentRules() throws EdgeRuleNotFoundException {
168         return getAllRules(schemaVersions.getDefaultVersion());
169     }
170
171     /**
172      * Retrieves all the nodes that contain multiple edge labels
173      *
174      * A lazy instantiation to retrieve all this info on first call
175      *
176      * @return  a set containing a list of strings where each string is
177      *          concatenated by a pipe (|) character such as aNodeType|bNodeType
178      */
179     public Set<String> getMultipleLabelKeys(){
180
181         if(multipleLabelKeys == null){
182             multipleLabelKeys = new HashSet<>();
183             try {
184                 final Multimap<String, EdgeRule> edges = this.getAllCurrentRules();
185                 if(edges == null || edges.isEmpty()){
186                     LOGGER.warn("Unable to find any edge rules for the latest version");
187                     return multipleLabelKeys;
188                 }
189                 edges.keySet().forEach(key -> {
190                     Collection<EdgeRule> rules = edges.get(key);
191                     if(rules.size() > 1){
192                         multipleLabelKeys.add(key);
193                     }
194                 });
195             } catch (EdgeRuleNotFoundException e) {
196                 LOGGER.info("For the latest schema version, unable to find any edges with multiple keys");
197             }
198         }
199
200         return multipleLabelKeys;
201     }
202     /**
203      * Gets list of all edge rules defined in the given version's schema
204      *
205      * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules associated with those types
206      *          where the key takes the form of
207      *                  {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
208      *          no rules are found.
209      *          ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
210      *                  buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
211      *
212      *  This is alphabetical order to normalize the keys, as sometimes there will be multiple
213      *  rules for a pair of node types but the from/to value in the json is flipped for some of them.
214      * @throws EdgeRuleNotFoundException if none found
215      */
216     public Multimap<String, EdgeRule> getAllRules(SchemaVersion v) throws EdgeRuleNotFoundException {
217         Multimap<String, EdgeRule> found = extractRules(null, v);
218         if (found.isEmpty()) {
219             throw new EdgeRuleNotFoundException("No rules found for version " + v.toString() + ".");
220         } else {
221             return found;
222         }
223     }
224
225     /**
226      * Finds the rules (if any) matching the given query criteria. If none, the returned Multimap
227      * will be empty.
228      *
229      * @param q - EdgeRuleQuery with filter criteria set
230      *
231      * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
232      *                  {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
233      *                  no rules are found.
234      *          ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
235      *                  buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
236      *
237      *  This is alphabetical order to normalize the keys, as sometimes there will be multiple
238      *  rules for a pair of node types but the from/to value in the json is flipped for some of them.
239      * @throws EdgeRuleNotFoundException if none found
240      */
241
242     public Multimap<String, EdgeRule> getRules(EdgeRuleQuery q) throws EdgeRuleNotFoundException {
243         Multimap<String, EdgeRule> found = null;
244         if(q.getVersion().isPresent()){
245             found = extractRules(q.getFilter(), q.getVersion().get());
246         } else {
247             found = extractRules(q.getFilter(), schemaVersions.getDefaultVersion());
248         }
249         if (found.isEmpty()) {
250             throw new EdgeRuleNotFoundException("No rules found for " + q.toString());
251         } else {
252             Multimap<String, EdgeRule> copy = ArrayListMultimap.create();
253             found.entries().stream().forEach((entry) -> {
254                 EdgeRule rule = new EdgeRule(entry.getValue());
255                 if (!q.getFromType().equals(rule.getFrom())) {
256                     /* To maintain backwards compatibility with old EdgeRules API,
257                      * where the direction of the returned EdgeRule would be
258                      * flipped (if necessary) to match the directionality of
259                      * the input params.
260                      * ie, If the rule is from=A,to=B,direction=OUT,
261                      * if the user asked (A,B) the direction would be OUT,
262                      * if they asked (B,A), it would be IN to match.
263                      */
264                     rule.flipDirection();
265                 }
266                 copy.put(entry.getKey(), rule);
267             });
268
269             return copy;
270         }
271     }
272
273
274
275     /**
276      * Gets the rule satisfying the given filter criteria. If there are more than one
277      * that match, return the default rule. If there is no clear default to return, or
278      * no rules match at all, error.
279      *
280      * @param q - EdgeRuleQuery with filter criteria set
281      * @return EdgeRule satisfying given criteria
282      * @throws EdgeRuleNotFoundException if none found that match
283      * @throws AmbiguousRuleChoiceException if multiple match but no way to choice one from them
284      *                  Specifically, if multiple node type pairs come back (ie bar|foo and asdf|foo,
285      *                                  no way to know which is appropriate over the others),
286      *                  or if there is a mix of Tree and Cousin edges because again there is no way to
287      *                                  know which is "defaulter" than the other.
288      *                  The default property only clarifies among multiple cousin edges of the same node pair,
289      *                          ex: which l-interface|logical-link rule to default to.
290      */
291     public EdgeRule getRule(EdgeRuleQuery q) throws EdgeRuleNotFoundException, AmbiguousRuleChoiceException {
292         Multimap<String, EdgeRule> found = null;
293         if(q.getVersion().isPresent()){
294             found = extractRules(q.getFilter(), q.getVersion().get());
295         } else {
296             found = extractRules(q.getFilter(), schemaVersions.getDefaultVersion());
297         }
298
299         if (found.isEmpty()) {
300             throw new EdgeRuleNotFoundException("No rule found for " + q.toString() + ".");
301         }
302
303         EdgeRule rule = null;
304         if (found.keys().size() == 1) { //only one found, cool we're done
305             for (Entry<String, EdgeRule> e : found.entries()) {
306                 rule = e.getValue();
307             }
308         } else {
309             rule = getDefaultRule(found);
310         }
311
312         if (rule == null) { //should never get here though
313             throw new EdgeRuleNotFoundException("No rule found for " + q.toString() + ".");
314         } else {
315             rule = new EdgeRule(rule);
316             if (!q.getFromType().equals(rule.getFrom())) {
317                 /* To maintain backwards compatibility with old EdgeRules API,
318                  * where the direction of the returned EdgeRule would be
319                  * flipped (if necessary) to match the directionality of
320                  * the input params.
321                  * ie, If the rule is from=A,to=B,direction=OUT,
322                  * if the user asked (A,B) the direction would be OUT,
323                  * if they asked (B,A), it would be IN to match.
324                  */
325                 rule.flipDirection();
326             }
327             return rule;
328         }
329     }
330
331     private EdgeRule getDefaultRule(Multimap<String, EdgeRule> found) throws AmbiguousRuleChoiceException {
332         if (found.keySet().size() > 1) { //ie multiple node pairs (a|c and b|c not just all a|c) case
333             StringBuilder sb = new StringBuilder();
334             for (String k : found.keySet()) {
335                 sb.append(k).append(" ");
336             }
337             throw new AmbiguousRuleChoiceException("No way to select single rule from these pairs: " + sb.toString() + ".");
338         }
339
340         int defaultCount = 0;
341         EdgeRule defRule = null;
342         for (Entry<String, EdgeRule> e : found.entries()) {
343             EdgeRule rule = e.getValue();
344             if (rule.isDefault()) {
345                 defaultCount++;
346                 defRule = rule;
347             }
348         }
349         if (defaultCount > 1) {
350             throw new AmbiguousRuleChoiceException("Multiple defaults found.");
351         } else if (defaultCount == 0) {
352             throw new AmbiguousRuleChoiceException("No default found.");
353         }
354
355         return defRule;
356     }
357     /**
358      * Checks if there exists any rule that satisfies the given filter criteria.
359      *
360      * @param q - EdgeRuleQuery with filter criteria set
361      * @return boolean
362      */
363     public boolean hasRule(EdgeRuleQuery q) {
364         if(q.getVersion().isPresent()){
365             return !extractRules(q.getFilter(), q.getVersion().get()).isEmpty();
366         } else {
367             return !extractRules(q.getFilter(), schemaVersions.getDefaultVersion()).isEmpty();
368         }
369     }
370
371     /**
372      * Gets all cousin rules for the given node type in the latest schema version.
373      *
374      * @param nodeType
375      * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
376      *                  {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
377      *                  no rules are found.
378      *          ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
379      *                  buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
380      *
381      *  This is alphabetical order to normalize the keys, as sometimes there will be multiple
382      *  rules for a pair of node types but the from/to value in the json is flipped for some of them.
383      */
384     public Multimap<String, EdgeRule> getCousinRules(String nodeType) {
385         return getCousinRules(nodeType, schemaVersions.getDefaultVersion()); //default to latest
386     }
387
388     public String[] retrieveCousinLabels(String nodeType){
389
390         Multimap<String, EdgeRule> cousinRules = getCousinRules(nodeType);
391         String[] cousinLabels = new String[cousinRules.size()];
392
393         return cousinRules.entries()
394             .stream()
395             .map(entry -> entry.getValue().getLabel())
396             .collect(Collectors.toList())
397             .toArray(cousinLabels);
398     }
399
400     public String[] retrieveCachedCousinLabels(String nodeType) throws ExecutionException {
401         return cousinLabelStore.get(nodeType);
402     }
403
404     /**
405      * Gets all cousin rules for the given node type in the given schema version.
406      *
407      * @param nodeType
408      * @param v - the version of the edge rules to query
409      * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
410      *                  {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
411      *                  no rules are found.
412      *          ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
413      *                  buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
414      *
415      *  This is alphabetical order to normalize the keys, as sometimes there will be multiple
416      *  rules for a pair of node types but the from/to value in the json is flipped for some of them.
417      */
418     public Multimap<String, EdgeRule> getCousinRules(String nodeType, SchemaVersion v) {
419         return extractRules(new EdgeRuleQuery.Builder(nodeType).edgeType(EdgeType.COUSIN).build().getFilter(), v);
420     }
421
422     /**
423      * Returns if the given node type has any cousin relationships in the current version.
424      * @param nodeType
425      * @return boolean
426      */
427     public boolean hasCousinRule(String nodeType) {
428         return hasCousinRule(nodeType, schemaVersions.getDefaultVersion());
429     }
430
431     /**
432      * Returns if the given node type has any cousin relationships in the given version.
433      * @param nodeType
434      * @return boolean
435      */
436     public boolean hasCousinRule(String nodeType, SchemaVersion v) {
437         return !getCousinRules(nodeType, v).isEmpty();
438     }
439
440     /**
441      * Gets all rules where "{given nodeType} contains {otherType}" in the latest schema version.
442      *
443      * @param nodeType - node type that is the container in the returned relationships
444      * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
445      *                  {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
446      *                  no rules are found.
447      *          ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
448      *                  buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
449      *
450      *  This is alphabetical order to normalize the keys, as sometimes there will be multiple
451      *  rules for a pair of node types but the from/to value in the json is flipped for some of them.
452      */
453     public Multimap<String, EdgeRule> getChildRules(String nodeType) {
454         return getChildRules(nodeType, schemaVersions.getDefaultVersion());
455     }
456
457     /**
458      * Gets all rules where "{given nodeType} contains {otherType}" in the given schema version.
459      *
460      * @param nodeType - node type that is the container in the returned relationships
461      * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
462      *                  {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
463      *                  no rules are found.
464      *          ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
465      *                  buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
466      *
467      *  This is alphabetical order to normalize the keys, as sometimes there will be multiple
468      *  rules for a pair of node types but the from/to value in the json is flipped for some of them.
469      */
470     public Multimap<String, EdgeRule> getChildRules(String nodeType, SchemaVersion v) {
471         Filter from = assembleFilterSegments(where(EdgeField.FROM.toString()).is(nodeType), getSameDirectionContainmentCriteria());
472         Filter to = assembleFilterSegments(where(EdgeField.TO.toString()).is(nodeType), getOppositeDirectionContainmentCriteria());
473         Filter total = from.or(to);
474
475         return extractRules(total, v);
476     }
477
478     /**
479      * Returns if the given node type has any child relationships (ie it contains another node type) in the current version.
480      * @param nodeType
481      * @return boolean
482      */
483     public boolean hasChildRule(String nodeType) {
484         return hasChildRule(nodeType, schemaVersions.getDefaultVersion());
485     }
486
487     /**
488      * Returns if the given node type has any child relationships (ie it contains another node type) in the given version.
489      * @param nodeType
490      * @return boolean
491      */
492     public boolean hasChildRule(String nodeType, SchemaVersion v) {
493         return !getChildRules(nodeType, v).isEmpty();
494     }
495
496     /**
497      * Gets all rules where "{given nodeType} is contained by {otherType}" in the latest schema version.
498      *
499      * @param nodeType - node type that is the containee in the returned relationships
500      * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
501      *                  {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
502      *                  no rules are found.
503      *          ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
504      *                  buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
505      *
506      *  This is alphabetical order to normalize the keys, as sometimes there will be multiple
507      *  rules for a pair of node types but the from/to value in the json is flipped for some of them.
508      */
509     public Multimap<String, EdgeRule> getParentRules(String nodeType) {
510         return getParentRules(nodeType, schemaVersions.getDefaultVersion());
511     }
512
513     /**
514      * Gets all rules where "{given nodeType} is contained by {otherType}" in the given schema version.
515      *
516      * @param nodeType - node type that is the containee in the returned relationships
517      * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
518      *                  {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
519      *                  no rules are found.
520      *          ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
521      *                  buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
522      *
523      *  This is alphabetical order to normalize the keys, as sometimes there will be multiple
524      *  rules for a pair of node types but the from/to value in the json is flipped for some of them.
525      */
526     public Multimap<String, EdgeRule> getParentRules(String nodeType, SchemaVersion v) {
527         Filter from = assembleFilterSegments(where(EdgeField.FROM.toString()).is(nodeType), getOppositeDirectionContainmentCriteria());
528         Filter to = assembleFilterSegments(where(EdgeField.TO.toString()).is(nodeType), getSameDirectionContainmentCriteria());
529         Filter total = from.or(to);
530
531         return extractRules(total, v);
532     }
533
534     /**
535      * Returns if the given node type has any parent relationships (ie it is contained by another node type) in the current version.
536      * @param nodeType
537      * @return boolean
538      */
539     public boolean hasParentRule(String nodeType) {
540         return hasParentRule(nodeType, schemaVersions.getDefaultVersion());
541     }
542
543     /**
544      * Returns if the given node type has any parent relationships (ie it is contained by another node type) in the given version.
545      * @param nodeType
546      * @return boolean
547      */
548     public boolean hasParentRule(String nodeType, SchemaVersion v) {
549         return !getParentRules(nodeType, v).isEmpty();
550     }
551
552         /**
553          * Applies the given filter to the DocumentContext(s) for the given version to extract
554          * edge rules, and converts this extracted information into the Multimap form
555          *
556          * @param filter - JsonPath filter to read the DocumentContexts with. May be null
557          *                                      to denote no filter, ie get all.
558          * @param v - The schema version to extract from
559          * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
560          *                      {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
561          *                      no rules are found.
562          *              ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
563          *                      buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
564          *
565          *      This is alphabetical order to normalize the keys, as sometimes there will be multiple
566          *      rules for a pair of node types but the from/to value in the json is flipped for some of them.
567          */
568     private Multimap<String, EdgeRule> extractRules(Filter filter, SchemaVersion v) {
569         SchemaFilter schemaFilter = new SchemaFilter(filter, v);
570         try {
571             return cacheFilterStore.get(schemaFilter);
572         } catch (ExecutionException e) {
573             LOGGER.info("Encountered exception during the retrieval of the rules");
574             return ArrayListMultimap.create();
575         }
576     }
577
578     public Multimap<String, EdgeRule> extractRules(SchemaFilter schemaFilter){
579         List<Map<String, String>> foundRules = new ArrayList<>();
580         List<DocumentContext> docs = versionJsonFilesMap.get(schemaFilter.getSchemaVersion());
581         if (docs != null) {
582             for (DocumentContext doc : docs) {
583                 if (schemaFilter.getFilter() == null) {
584                     foundRules.addAll(doc.read(READ_ALL_START));
585                 } else {
586                     foundRules.addAll(doc.read(READ_START, Filter.parse(schemaFilter.getFilter())));
587                 }
588             }
589         }
590
591         return convertToEdgeRules(foundRules);
592     }
593
594         //-----filter building helpers-----//
595         /**
596          * ANDs together the given start criteria with each criteria in the pieces list, and
597          * then ORs together these segments into one filter.
598          *
599          * JsonPath doesn't have an OR method on Criteria, only on Filters, so assembling
600          * a complete filter requires this sort of roundabout construction.
601          *
602          * @param start - Criteria of the form where(from/to).is(nodeType)
603          *                                      (ie the start of any A&AI edge rule query)
604          * @param pieces - Other Criteria to be applied
605          * @return Filter constructed from the given Criteria
606          */
607         private Filter assembleFilterSegments(Criteria start, List<Criteria> pieces) {
608                 List<Filter> segments = new ArrayList<>();
609                 for (Criteria c : pieces) {
610                         segments.add(filter(start).and(c));
611                 }
612                 Filter assembled = segments.remove(0);
613                 for (Filter f : segments) {
614                         assembled = assembled.or(f);
615                 }
616                 return assembled;
617         }
618
619         /**
620          * Builds the sub-Criteria for a containment edge rule query where the direction
621          * and containment fields must match.
622          *
623          * Used for getChildRules() where the container node type is in the "from" position and
624          * for getParentRules() where the containee type is in the "to" position.
625          *
626          * @return List<Criteria> covering property permutations defined with either notation or explicit direction
627          */
628         private List<Criteria> getSameDirectionContainmentCriteria() {
629                 List<Criteria> crits = new ArrayList<>();
630
631                 crits.add(where(EdgeField.CONTAINS.toString()).is(DirectionNotation.DIRECTION.toString()));
632
633                 crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.OUT.toString())
634                                 .and(EdgeField.CONTAINS.toString()).is(Direction.OUT.toString()));
635
636                 crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.IN.toString())
637                                 .and(EdgeField.CONTAINS.toString()).is(Direction.IN.toString()));
638
639                 return crits;
640         }
641
642         /**
643          * Builds the sub-Criteria for a containment edge rule query where the direction
644          * and containment fields must not match.
645          *
646          * Used for getChildRules() where the container node type is in the "to" position and
647          * for getParentRules() where the containee type is in the "from" position.
648          *
649          * @return List<Criteria> covering property permutations defined with either notation or explicit direction
650          */
651         private List<Criteria> getOppositeDirectionContainmentCriteria() {
652                 List<Criteria> crits = new ArrayList<>();
653
654                 crits.add(where(EdgeField.CONTAINS.toString()).is(DirectionNotation.OPPOSITE.toString()));
655
656                 crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.OUT.toString())
657                                 .and(EdgeField.CONTAINS.toString()).is(Direction.IN.toString()));
658
659                 crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.IN.toString())
660                                 .and(EdgeField.CONTAINS.toString()).is(Direction.OUT.toString()));
661
662                 return crits;
663         }
664
665         //-----rule packaging helpers-----//
666         /**
667          * Converts the raw output from reading the json file to the Multimap<String key, EdgeRule> format
668          *
669          * @param allFound - raw edge rule output read from json file(s)
670          *                      (could be empty if none found)
671          * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
672          *                      {alphabetically first nodetype}|{alphabetically second nodetype}. Will be empty if input
673          *                      was empty.
674          *              ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
675          *                      buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
676          *
677          *      This is alphabetical order to normalize the keys, as sometimes there will be multiple
678          *      rules for a pair of node types but the from/to value in the json is flipped for some of them.
679          */
680         private Multimap<String, EdgeRule> convertToEdgeRules(List<Map<String, String>> allFound) {
681                 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
682
683                 TypeAlphabetizer alpher = new TypeAlphabetizer();
684
685                 for (Map<String, String> raw : allFound) {
686                         EdgeRule converted = new EdgeRule(raw);
687                         if (converted.getFrom().equals(converted.getTo())) {
688                                 /* the way the code worked in the past was with outs and
689                                  * when we switched it to in the same-node-type to
690                                  * same-node-type parent child edges were failing because all
691                                  * of the calling code would pass the parent as the left argument,
692                                  * so it was either in that method swap the parent/child,
693                                  * flip the edge rule or make all callers swap. the last seemed
694                                  * like a bad idea. and felt like the edge flip was the better
695                                  * of the remaining 2 */
696                                 converted.flipDirection();
697                         }
698                         String alphabetizedKey = alpher.buildAlphabetizedKey(raw.get(EdgeField.FROM.toString()), raw.get(EdgeField.TO.toString()));
699                         rules.put(alphabetizedKey, converted);
700                 }
701
702                 return rules;
703         }
704
705 }