2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.aai.edges;
23 import static com.jayway.jsonpath.Criteria.where;
24 import static com.jayway.jsonpath.Filter.filter;
26 import com.google.common.cache.CacheBuilder;
27 import com.google.common.cache.CacheLoader;
28 import com.google.common.cache.LoadingCache;
29 import com.google.common.collect.ArrayListMultimap;
30 import com.google.common.collect.Multimap;
31 import com.jayway.jsonpath.Criteria;
32 import com.jayway.jsonpath.DocumentContext;
33 import com.jayway.jsonpath.Filter;
35 import java.io.IOException;
37 import java.util.Map.Entry;
38 import java.util.concurrent.ExecutionException;
39 import java.util.stream.Collectors;
41 import javax.annotation.PostConstruct;
43 import org.apache.tinkerpop.gremlin.structure.Direction;
44 import org.onap.aai.edges.enums.DirectionNotation;
45 import org.onap.aai.edges.enums.EdgeField;
46 import org.onap.aai.edges.enums.EdgeType;
47 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
48 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
49 import org.onap.aai.setup.SchemaVersion;
50 import org.onap.aai.setup.SchemaVersions;
51 import org.onap.aai.setup.Translator;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54 import org.springframework.beans.factory.annotation.Autowired;
55 import org.springframework.stereotype.Component;
58 * EdgeIngestor - ingests A&AI edge rule schema files per given config, serves that edge rule
59 * information, including allowing various filters to extract particular rules.
62 public class EdgeIngestor {
63 private static final Logger LOGGER = LoggerFactory.getLogger(EdgeIngestor.class);
64 private Map<SchemaVersion, List<DocumentContext>> versionJsonFilesMap = new TreeMap<>();
65 private static final String READ_START = "$.rules.[?]";
66 private static final String READ_ALL_START = "$.rules.*";
67 private SchemaVersions schemaVersions;
69 Map<SchemaVersion, List<String>> filesToIngest;
71 private Set<String> multipleLabelKeys;
73 private LoadingCache<SchemaFilter, Multimap<String, EdgeRule>> cacheFilterStore;
75 private LoadingCache<String, String[]> cousinLabelStore;
77 private Set<Translator> translators;
80 public EdgeIngestor(Set<Translator> translatorSet) {
81 this.translators = translatorSet;
85 public void initialize() {
87 for (Translator translator : translators) {
89 LOGGER.debug("Processing the translator");
90 translateAll(translator);
92 } catch (Exception e) {
93 LOGGER.error("Error while Processing the translator" + e.getMessage());
94 throw new ExceptionInInitializerError("EdgeIngestor could not ingest schema");
97 if (versionJsonFilesMap.isEmpty() || schemaVersions == null) {
98 throw new ExceptionInInitializerError("EdgeIngestor could not ingest edgerules");
102 public void translateAll(Translator translator) throws ExceptionInInitializerError {
104 * Use SchemaVersions from the Translator
106 this.schemaVersions = translator.getSchemaVersions();
107 List<SchemaVersion> schemaVersionList = this.schemaVersions.getVersions();
108 List<String> jsonPayloads = null;
109 JsonIngestor ji = new JsonIngestor();
110 Map<SchemaVersion, List<String>> edgeRulesToIngest = new HashMap<>(); // Obtain a map of schema versions to a
111 // list of strings. One List per key
113 // Add to the map the JSON file per version.
114 for (SchemaVersion version : schemaVersionList) {
115 LOGGER.debug("Version being processed" + version);
116 // If the flag is set to not use the local files, obtain the Json from the service.
118 jsonPayloads = translator.getJsonPayload(version); // need to change this - need to receive the json
120 } catch (IOException e) {
121 LOGGER.error("Error in retrieving the JSON Payload" + e.getMessage());
122 throw new ExceptionInInitializerError("EdgeIngestor could not ingest schema");
124 if (jsonPayloads == null || jsonPayloads.isEmpty()) {
127 LOGGER.debug("Retrieved json from SchemaService");
128 edgeRulesToIngest.put(version, jsonPayloads);
130 versionJsonFilesMap = ji.ingestContent(edgeRulesToIngest);
132 this.cacheFilterStore = CacheBuilder.newBuilder().maximumSize(2000)
133 .build(new CacheLoader<SchemaFilter, Multimap<String, EdgeRule>>() {
135 public Multimap<String, EdgeRule> load(SchemaFilter key) {
136 return extractRules(key);
140 this.cousinLabelStore = CacheBuilder.newBuilder().maximumSize(50).build(new CacheLoader<String, String[]>() {
142 public String[] load(String key) throws Exception {
143 return retrieveCousinLabels(key);
148 // //-----methods for getting rule info-----//
151 * Gets list of all edge rules defined in the latest version's schema
153 * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules associated with those types
154 * where the key takes the form of
155 * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
156 * no rules are found.
157 * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
158 * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
160 * This is alphabetical order to normalize the keys, as sometimes there will be multiple
161 * rules for a pair of node types but the from/to value in the json is flipped for some of them.
162 * @throws EdgeRuleNotFoundException if none found
164 public Multimap<String, EdgeRule> getAllCurrentRules() throws EdgeRuleNotFoundException {
165 return getAllRules(schemaVersions.getDefaultVersion());
169 * Retrieves all the nodes that contain multiple edge labels
171 * A lazy instantiation to retrieve all this info on first call
173 * @return a set containing a list of strings where each string is
174 * concatenated by a pipe (|) character such as aNodeType|bNodeType
176 public Set<String> getMultipleLabelKeys() {
178 if (multipleLabelKeys == null) {
179 multipleLabelKeys = new HashSet<>();
181 final Multimap<String, EdgeRule> edges = this.getAllCurrentRules();
182 if (edges == null || edges.isEmpty()) {
183 LOGGER.warn("Unable to find any edge rules for the latest version");
184 return multipleLabelKeys;
186 edges.keySet().forEach(key -> {
187 Collection<EdgeRule> rules = edges.get(key);
188 if (rules.size() > 1) {
189 multipleLabelKeys.add(key);
192 } catch (EdgeRuleNotFoundException e) {
193 LOGGER.info("For the latest schema version, unable to find any edges with multiple keys");
197 return multipleLabelKeys;
201 * Gets list of all edge rules defined in the given version's schema
203 * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules associated with those types
204 * where the key takes the form of
205 * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
206 * no rules are found.
207 * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
208 * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
210 * This is alphabetical order to normalize the keys, as sometimes there will be multiple
211 * rules for a pair of node types but the from/to value in the json is flipped for some of them.
212 * @throws EdgeRuleNotFoundException if none found
214 public Multimap<String, EdgeRule> getAllRules(SchemaVersion v) throws EdgeRuleNotFoundException {
215 Multimap<String, EdgeRule> found = extractRules(null, v);
216 if (found.isEmpty()) {
217 throw new EdgeRuleNotFoundException("No rules found for version " + v.toString() + ".");
224 * Finds the rules (if any) matching the given query criteria. If none, the returned Multimap
227 * @param q - EdgeRuleQuery with filter criteria set
229 * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
230 * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
231 * no rules are found.
232 * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
233 * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
235 * This is alphabetical order to normalize the keys, as sometimes there will be multiple
236 * rules for a pair of node types but the from/to value in the json is flipped for some of them.
237 * @throws EdgeRuleNotFoundException if none found
240 public Multimap<String, EdgeRule> getRules(EdgeRuleQuery q) throws EdgeRuleNotFoundException {
241 Multimap<String, EdgeRule> found = null;
242 Optional<SchemaVersion> versionOpt = q.getVersion();
243 if (versionOpt.isPresent()) {
244 found = extractRules(q.getFilter(), versionOpt.get());
246 found = extractRules(q.getFilter(), schemaVersions.getDefaultVersion());
248 if (found.isEmpty()) {
249 throw new EdgeRuleNotFoundException("No rules found for " + q.toString());
251 Multimap<String, EdgeRule> copy = ArrayListMultimap.create();
252 found.entries().stream().forEach((entry) -> {
253 EdgeRule rule = new EdgeRule(entry.getValue());
254 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
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.
264 rule.flipDirection();
266 copy.put(entry.getKey(), rule);
274 * Gets the rule satisfying the given filter criteria. If there are more than one
275 * that match, return the default rule. If there is no clear default to return, or
276 * no rules match at all, error.
278 * @param q - EdgeRuleQuery with filter criteria set
279 * @return EdgeRule satisfying given criteria
280 * @throws EdgeRuleNotFoundException if none found that match
281 * @throws AmbiguousRuleChoiceException if multiple match but no way to choice one from them
282 * Specifically, if multiple node type pairs come back (ie bar|foo and asdf|foo,
283 * no way to know which is appropriate over the others),
284 * or if there is a mix of Tree and Cousin edges because again there is no way to
285 * know which is "defaulter" than the other.
286 * The default property only clarifies among multiple cousin edges of the same node pair,
287 * ex: which l-interface|logical-link rule to default to.
289 public EdgeRule getRule(EdgeRuleQuery q) throws EdgeRuleNotFoundException, AmbiguousRuleChoiceException {
290 Multimap<String, EdgeRule> found = null;
291 Optional<SchemaVersion> versionOpt = q.getVersion();
292 if (versionOpt.isPresent()) {
293 found = extractRules(q.getFilter(), versionOpt.get());
295 found = extractRules(q.getFilter(), schemaVersions.getDefaultVersion());
298 if (found.isEmpty()) {
299 throw new EdgeRuleNotFoundException("No rule found for " + q.toString() + ".");
302 EdgeRule rule = null;
303 if (found.keys().size() == 1) { // only one found, cool we're done
304 for (Entry<String, EdgeRule> e : found.entries()) {
308 rule = getDefaultRule(found);
311 if (rule == null) { // should never get here though
312 throw new EdgeRuleNotFoundException("No rule found for " + q.toString() + ".");
314 rule = new EdgeRule(rule);
315 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
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.
325 rule.flipDirection();
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(" ");
337 throw new AmbiguousRuleChoiceException(
338 "No way to select single rule from these pairs: " + sb.toString() + ".");
341 int defaultCount = 0;
342 EdgeRule defRule = null;
343 for (Entry<String, EdgeRule> e : found.entries()) {
344 EdgeRule rule = e.getValue();
345 if (rule.isDefault()) {
350 if (defaultCount > 1) {
351 throw new AmbiguousRuleChoiceException("Multiple defaults found.");
352 } else if (defaultCount == 0) {
353 throw new AmbiguousRuleChoiceException("No default found.");
360 * Checks if there exists any rule that satisfies the given filter criteria.
362 * @param q - EdgeRuleQuery with filter criteria set
365 public boolean hasRule(EdgeRuleQuery q) {
366 Optional<SchemaVersion> versionOpt = q.getVersion();
367 if (versionOpt.isPresent()) {
368 return !extractRules(q.getFilter(), versionOpt.get()).isEmpty();
370 return !extractRules(q.getFilter(), schemaVersions.getDefaultVersion()).isEmpty();
375 * Gets all cousin rules for the given node type in the latest schema version.
378 * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
379 * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
380 * no rules are found.
381 * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
382 * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
384 * This is alphabetical order to normalize the keys, as sometimes there will be multiple
385 * rules for a pair of node types but the from/to value in the json is flipped for some of them.
387 public Multimap<String, EdgeRule> getCousinRules(String nodeType) {
388 return getCousinRules(nodeType, schemaVersions.getDefaultVersion()); // default to latest
391 public String[] retrieveCousinLabels(String nodeType) {
393 Multimap<String, EdgeRule> cousinRules = getCousinRules(nodeType);
394 String[] cousinLabels = new String[cousinRules.size()];
396 return cousinRules.entries().stream().map(entry -> entry.getValue().getLabel()).collect(Collectors.toList())
397 .toArray(cousinLabels);
400 public String[] retrieveCachedCousinLabels(String nodeType) throws ExecutionException {
401 return cousinLabelStore.get(nodeType);
405 * Gets all cousin rules for the given node type in the given schema version.
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"
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.
418 public Multimap<String, EdgeRule> getCousinRules(String nodeType, SchemaVersion v) {
419 return extractRules(new EdgeRuleQuery.Builder(nodeType).edgeType(EdgeType.COUSIN).build().getFilter(), v);
423 * Returns if the given node type has any cousin relationships in the current version.
428 public boolean hasCousinRule(String nodeType) {
429 return hasCousinRule(nodeType, schemaVersions.getDefaultVersion());
433 * Returns if the given node type has any cousin relationships in the given version.
438 public boolean hasCousinRule(String nodeType, SchemaVersion v) {
439 return !getCousinRules(nodeType, v).isEmpty();
443 * Gets all rules where "{given nodeType} contains {otherType}" in the latest schema version.
445 * @param nodeType - node type that is the container in the returned relationships
446 * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
447 * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
448 * no rules are found.
449 * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
450 * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
452 * This is alphabetical order to normalize the keys, as sometimes there will be multiple
453 * rules for a pair of node types but the from/to value in the json is flipped for some of them.
455 public Multimap<String, EdgeRule> getChildRules(String nodeType) {
456 return getChildRules(nodeType, schemaVersions.getDefaultVersion());
460 * Gets all rules where "{given nodeType} contains {otherType}" in the given schema version.
462 * @param nodeType - node type that is the container in the returned relationships
463 * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
464 * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
465 * no rules are found.
466 * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
467 * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
469 * This is alphabetical order to normalize the keys, as sometimes there will be multiple
470 * rules for a pair of node types but the from/to value in the json is flipped for some of them.
472 public Multimap<String, EdgeRule> getChildRules(String nodeType, SchemaVersion v) {
473 Filter from = assembleFilterSegments(where(EdgeField.FROM.toString()).is(nodeType),
474 getSameDirectionContainmentCriteria());
475 Filter to = assembleFilterSegments(where(EdgeField.TO.toString()).is(nodeType),
476 getOppositeDirectionContainmentCriteria());
477 Filter total = from.or(to);
479 return extractRules(total, v);
483 * Returns if the given node type has any child relationships (ie it contains another node type) in the current
489 public boolean hasChildRule(String nodeType) {
490 return hasChildRule(nodeType, schemaVersions.getDefaultVersion());
494 * Returns if the given node type has any child relationships (ie it contains another node type) in the given
500 public boolean hasChildRule(String nodeType, SchemaVersion v) {
501 return !getChildRules(nodeType, v).isEmpty();
505 * Gets all rules where "{given nodeType} is contained by {otherType}" in the latest schema version.
507 * @param nodeType - node type that is the containee in the returned relationships
508 * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
509 * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
510 * no rules are found.
511 * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
512 * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
514 * This is alphabetical order to normalize the keys, as sometimes there will be multiple
515 * rules for a pair of node types but the from/to value in the json is flipped for some of them.
517 public Multimap<String, EdgeRule> getParentRules(String nodeType) {
518 return getParentRules(nodeType, schemaVersions.getDefaultVersion());
522 * Gets all rules where "{given nodeType} is contained by {otherType}" in the given schema version.
524 * @param nodeType - node type that is the containee in the returned relationships
525 * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
526 * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
527 * no rules are found.
528 * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
529 * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
531 * This is alphabetical order to normalize the keys, as sometimes there will be multiple
532 * rules for a pair of node types but the from/to value in the json is flipped for some of them.
534 public Multimap<String, EdgeRule> getParentRules(String nodeType, SchemaVersion v) {
535 Filter from = assembleFilterSegments(where(EdgeField.FROM.toString()).is(nodeType),
536 getOppositeDirectionContainmentCriteria());
537 Filter to = assembleFilterSegments(where(EdgeField.TO.toString()).is(nodeType),
538 getSameDirectionContainmentCriteria());
539 Filter total = from.or(to);
541 return extractRules(total, v);
545 * Returns if the given node type has any parent relationships (ie it is contained by another node type) in the
551 public boolean hasParentRule(String nodeType) {
552 return hasParentRule(nodeType, schemaVersions.getDefaultVersion());
556 * Returns if the given node type has any parent relationships (ie it is contained by another node type) in the
562 public boolean hasParentRule(String nodeType, SchemaVersion v) {
563 return !getParentRules(nodeType, v).isEmpty();
567 * Applies the given filter to the DocumentContext(s) for the given version to extract
568 * edge rules, and converts this extracted information into the Multimap form
570 * @param filter - JsonPath filter to read the DocumentContexts with. May be null
571 * to denote no filter, ie get all.
572 * @param v - The schema version to extract from
573 * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
574 * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if
575 * no rules are found.
576 * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
577 * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
579 * This is alphabetical order to normalize the keys, as sometimes there will be multiple
580 * rules for a pair of node types but the from/to value in the json is flipped for some of them.
582 private Multimap<String, EdgeRule> extractRules(Filter filter, SchemaVersion v) {
583 SchemaFilter schemaFilter = new SchemaFilter(filter, v);
585 return cacheFilterStore.get(schemaFilter);
586 } catch (ExecutionException e) {
587 LOGGER.info("Encountered exception during the retrieval of the rules");
588 return ArrayListMultimap.create();
592 public Multimap<String, EdgeRule> extractRules(SchemaFilter schemaFilter) {
593 List<Map<String, String>> foundRules = new ArrayList<>();
594 List<DocumentContext> docs = versionJsonFilesMap.get(schemaFilter.getSchemaVersion());
596 for (DocumentContext doc : docs) {
597 if (schemaFilter.getFilter() == null) {
598 foundRules.addAll(doc.read(READ_ALL_START));
600 foundRules.addAll(doc.read(READ_START, Filter.parse(schemaFilter.getFilter())));
605 return convertToEdgeRules(foundRules);
608 // -----filter building helpers-----//
610 * ANDs together the given start criteria with each criteria in the pieces list, and
611 * then ORs together these segments into one filter.
613 * JsonPath doesn't have an OR method on Criteria, only on Filters, so assembling
614 * a complete filter requires this sort of roundabout construction.
616 * @param start - Criteria of the form where(from/to).is(nodeType)
617 * (ie the start of any A&AI edge rule query)
618 * @param pieces - Other Criteria to be applied
619 * @return Filter constructed from the given Criteria
621 private Filter assembleFilterSegments(Criteria start, List<Criteria> pieces) {
622 List<Filter> segments = new ArrayList<>();
623 for (Criteria c : pieces) {
624 segments.add(filter(start).and(c));
626 Filter assembled = segments.remove(0);
627 for (Filter f : segments) {
628 assembled = assembled.or(f);
634 * Builds the sub-Criteria for a containment edge rule query where the direction
635 * and containment fields must match.
637 * Used for getChildRules() where the container node type is in the "from" position and
638 * for getParentRules() where the containee type is in the "to" position.
640 * @return List<Criteria> covering property permutations defined with either notation or explicit direction
642 private List<Criteria> getSameDirectionContainmentCriteria() {
643 List<Criteria> crits = new ArrayList<>();
645 crits.add(where(EdgeField.CONTAINS.toString()).is(DirectionNotation.DIRECTION.toString()));
647 crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.OUT.toString()).and(EdgeField.CONTAINS.toString())
648 .is(Direction.OUT.toString()));
650 crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.IN.toString()).and(EdgeField.CONTAINS.toString())
651 .is(Direction.IN.toString()));
657 * Builds the sub-Criteria for a containment edge rule query where the direction
658 * and containment fields must not match.
660 * Used for getChildRules() where the container node type is in the "to" position and
661 * for getParentRules() where the containee type is in the "from" position.
663 * @return List<Criteria> covering property permutations defined with either notation or explicit direction
665 private List<Criteria> getOppositeDirectionContainmentCriteria() {
666 List<Criteria> crits = new ArrayList<>();
668 crits.add(where(EdgeField.CONTAINS.toString()).is(DirectionNotation.OPPOSITE.toString()));
670 crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.OUT.toString()).and(EdgeField.CONTAINS.toString())
671 .is(Direction.IN.toString()));
673 crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.IN.toString()).and(EdgeField.CONTAINS.toString())
674 .is(Direction.OUT.toString()));
679 // -----rule packaging helpers-----//
681 * Converts the raw output from reading the json file to the Multimap<String key, EdgeRule> format
683 * @param allFound - raw edge rule output read from json file(s)
684 * (could be empty if none found)
685 * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of
686 * {alphabetically first nodetype}|{alphabetically second nodetype}. Will be empty if input
688 * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link"
689 * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link"
691 * This is alphabetical order to normalize the keys, as sometimes there will be multiple
692 * rules for a pair of node types but the from/to value in the json is flipped for some of them.
694 private Multimap<String, EdgeRule> convertToEdgeRules(List<Map<String, String>> allFound) {
695 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
697 TypeAlphabetizer alpher = new TypeAlphabetizer();
699 for (Map<String, String> raw : allFound) {
700 EdgeRule converted = new EdgeRule(raw);
701 if (converted.getFrom().equals(converted.getTo())) {
703 * the way the code worked in the past was with outs and
704 * when we switched it to in the same-node-type to
705 * same-node-type parent child edges were failing because all
706 * of the calling code would pass the parent as the left argument,
707 * so it was either in that method swap the parent/child,
708 * flip the edge rule or make all callers swap. the last seemed
709 * like a bad idea. and felt like the edge flip was the better
712 converted.flipDirection();
714 String alphabetizedKey =
715 alpher.buildAlphabetizedKey(raw.get(EdgeField.FROM.toString()), raw.get(EdgeField.TO.toString()));
716 rules.put(alphabetizedKey, converted);