From 2e4f21dea97049f4de34fcfd789f461882e24602 Mon Sep 17 00:00:00 2001 From: "Maharajh, Robby (rx2202)" Date: Fri, 1 Dec 2017 10:41:41 -0500 Subject: Added support for Multiple Edges Issue-ID: AAI-524 Change-Id: I33dd31315992e2efb2d6d390ffc523655b55a84c Signed-off-by: Maharajh, Robby (rx2202) --- .../aai/query/builder/GraphTraversalBuilder.java | 220 +++++--- .../aai/query/builder/GremlinQueryBuilder.java | 165 ++++-- .../onap/aai/query/builder/GremlinTraversal.java | 32 +- .../org/onap/aai/query/builder/GremlinUnique.java | 10 +- .../org/onap/aai/query/builder/QueryBuilder.java | 99 +++- .../org/onap/aai/query/builder/TraversalQuery.java | 35 +- .../main/java/org/onap/aai/rest/db/HttpEntry.java | 5 +- .../onap/aai/serialization/db/DBSerializer.java | 266 +++++---- .../onap/aai/serialization/db/EdgeProperty.java | 19 +- .../org/onap/aai/serialization/db/EdgeRule.java | 34 +- .../org/onap/aai/serialization/db/EdgeRules.java | 611 +++++++++++++++------ .../exceptions/MultipleEdgeRuleFoundException.java | 40 ++ .../db/exceptions/NoEdgeRuleFoundException.java | 6 +- .../aai/serialization/db/util/VersionChecker.java | 39 ++ .../aai/serialization/queryformats/RawFormat.java | 31 +- .../AAIFormatQueryResultFormatNotSupported.java | 40 ++ 16 files changed, 1184 insertions(+), 468 deletions(-) create mode 100644 aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/MultipleEdgeRuleFoundException.java create mode 100644 aai-core/src/main/java/org/onap/aai/serialization/db/util/VersionChecker.java create mode 100644 aai-core/src/main/java/org/onap/aai/serialization/queryformats/exceptions/AAIFormatQueryResultFormatNotSupported.java (limited to 'aai-core/src/main/java') diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java b/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java index 8a585dae..82079ad7 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java @@ -22,9 +22,8 @@ package org.onap.aai.query.builder; import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -74,6 +73,18 @@ public abstract class GraphTraversalBuilder extends QueryBuilder { } + /** + * Instantiates a new graph traversal builder. + * + * @param loader the loader + */ + public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, EdgeRules edgeRules) { + super(loader, source); + this.edgeRules = edgeRules; + traversal = (GraphTraversal) __.start(); + + } + /** * Instantiates a new graph traversal builder. * @@ -86,34 +97,28 @@ public abstract class GraphTraversalBuilder extends QueryBuilder { traversal = (GraphTraversal) __.__(start); } - - /** - * @{inheritDoc} - */ - @Override - public QueryBuilder getVerticesByIndexedProperty(String key, Object value) { - return this.getVerticesByProperty(key, value); - } - /** - * @{inheritDoc} + * Instantiates a new graph traversal builder. + * + * @param loader the loader + * @param start the start */ - @Override - public QueryBuilder getVerticesByIndexedProperty(String key, List values) { - return this.getVerticesByProperty(key, values); + public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start, EdgeRules edgeRules) { + super(loader, source, start); + this.edgeRules = edgeRules; + traversal = (GraphTraversal) __.__(start); + } - + /** * @{inheritDoc} */ @Override public QueryBuilder getVerticesByProperty(String key, Object value) { - //this is because the index is registered as an Integer - value = this.correctObjectType(value); - - traversal.has(key, value); + // correct value call because the index is registered as an Integer + traversal.has(key, this.correctObjectType(value)); stepIndex++; return (QueryBuilder) this; @@ -151,10 +156,10 @@ public abstract class GraphTraversalBuilder extends QueryBuilder { * @{inheritDoc} */ @Override - public QueryBuilder getTypedVerticesByMap(String type, LinkedHashMap map) { + public QueryBuilder getTypedVerticesByMap(String type, Map map) { - for (String key : map.keySet()) { - traversal.has(key, map.get(key)); + for (Map.Entry es : map.entrySet()) { + traversal.has(es.getKey(), es.getValue()); stepIndex++; } traversal.has(AAIProperties.NODE_TYPE, type); @@ -162,16 +167,6 @@ public abstract class GraphTraversalBuilder extends QueryBuilder { return (QueryBuilder) this; } - /** - * @{inheritDoc} - */ - @Override - public QueryBuilder createDBQuery(Introspector obj) { - this.createKeyQuery(obj); - this.createContainerQuery(obj); - return (QueryBuilder) this; - } - /** * @{inheritDoc} */ @@ -257,70 +252,78 @@ public abstract class GraphTraversalBuilder extends QueryBuilder { * @{inheritDoc} */ @Override - public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException, NoEdgeRuleFoundException { + public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException { String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT); if ("true".equals(isAbstractType)) { markParentBoundary(); traversal.union(handleAbstractEdge(type, parent, child)); - stepIndex += 1; + stepIndex++; } else { - this.edgeQueryToVertex(type, parent, child); + this.edgeQueryToVertex(type, parent, child, null); } return (QueryBuilder) this; } - + + /** + * + * @{inheritDoc} + */ + @Override + public QueryBuilder createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in, List labels) throws AAIException { + this.edgeQueryToVertex(type, out, in, labels); + return (QueryBuilder) this; + } + + private Traversal[] handleAbstractEdge(EdgeType type, Introspector abstractParent, Introspector child) throws AAIException, NoEdgeRuleFoundException { String childName = child.getDbName(); String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS); String[] inheritors = inheritorMetadata.split(","); - Traversal[] unionTraversals = new Traversal[inheritors.length]; - int traversalIndex = 0; + List> unionTraversals = new ArrayList<>(inheritors.length); + for (int i = 0; i < inheritors.length; i++) { String inheritor = inheritors[i]; if (edgeRules.hasEdgeRule(inheritor, childName) || edgeRules.hasEdgeRule(childName, inheritor)) { - EdgeRule rule = edgeRules.getEdgeRule(type, inheritor, childName); + Map rules = edgeRules.getEdgeRules(type, inheritor, childName); GraphTraversal innerTraversal = __.start(); - if (rule.getDirection().equals(Direction.OUT)) { - innerTraversal.out(rule.getLabel()); - } else { - innerTraversal.in(rule.getLabel()); + + final List inLabels = new ArrayList<>(); + final List outLabels = new ArrayList<>(); + + rules.forEach((k,v) -> { + if (v.getDirection().equals(Direction.IN)) { + inLabels.add(k); + } else { + outLabels.add(k); + } + } ); + + if (inLabels.isEmpty() && !outLabels.isEmpty()) { + innerTraversal.out(outLabels.toArray(new String[outLabels.size()])); + } else if (outLabels.isEmpty() && !inLabels.isEmpty()) { + innerTraversal.in(inLabels.toArray(new String[inLabels.size()])); + } else { + innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), __.in(inLabels.toArray(new String[inLabels.size()]))); } + innerTraversal.has(AAIProperties.NODE_TYPE, childName); - unionTraversals[traversalIndex] = innerTraversal; - traversalIndex++; + unionTraversals.add(innerTraversal); } } - if (traversalIndex < inheritors.length) { - Traversal[] temp = Arrays.copyOfRange(unionTraversals, 0, traversalIndex); - unionTraversals = temp; - } - return unionTraversals; - } - /** - * @throws NoEdgeRuleFoundException - * @throws AAIException - * @{inheritDoc} - */ - @Override - public QueryBuilder createEdgeTraversal(EdgeType type, Vertex parent, Introspector child) throws AAIException, NoEdgeRuleFoundException { - String nodeType = parent.property(AAIProperties.NODE_TYPE).orElse(null); - Introspector parentObj = loader.introspectorFromName(nodeType); - this.edgeQueryToVertex(type, parentObj, child); - return (QueryBuilder) this; - + return unionTraversals.toArray(new Traversal[unionTraversals.size()]); } - - @Override - public QueryBuilder getEdgesBetween(EdgeType type, String outNodeType, String inNodeType) throws AAIException { + + public QueryBuilder getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType, List labels) throws AAIException { Introspector outObj = loader.introspectorFromName(outNodeType); Introspector inObj = loader.introspectorFromName(inNodeType); - this.edgeQuery(type, outObj, inObj); - - return (QueryBuilder)this; + this.edgeQuery(type, outObj, inObj, labels); + return (QueryBuilder)this; } + + /** * @{inheritDoc} */ @@ -477,12 +480,12 @@ public abstract class GraphTraversalBuilder extends QueryBuilder { /** * Edge query. * - * @param outType the out type - * @param inType the in type + * @param outObj the out type + * @param inObj the in type * @throws NoEdgeRuleFoundException * @throws AAIException */ - private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj) throws AAIException, NoEdgeRuleFoundException { + private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List labels) throws AAIException { String outType = outObj.getDbName(); String inType = inObj.getDbName(); @@ -493,13 +496,38 @@ public abstract class GraphTraversalBuilder extends QueryBuilder { inType = inObj.getChildDBName(); } markParentBoundary(); - EdgeRule rule = edgeRules.getEdgeRule(type, outType, inType); - if (rule.getDirection().equals(Direction.OUT)) { - traversal.out(rule.getLabel()); + Map rules; + if (labels == null) { + rules = edgeRules.getEdgeRules(type, outType, inType); } else { - traversal.in(rule.getLabel()); + rules = edgeRules.getEdgeRulesWithLabels(type, outType, inType, labels); + } + + final List inLabels = new ArrayList<>(); + final List outLabels = new ArrayList<>(); + + rules.forEach((k, edgeRule) -> { + if (labels != null && !labels.contains(k)) { + return; + } else { + if (edgeRule.getDirection().equals(Direction.IN)) { + inLabels.add(edgeRule.getLabel()); + } else { + outLabels.add(edgeRule.getLabel()); + } + } + }); + + if (inLabels.isEmpty() && !outLabels.isEmpty()) { + traversal.out(outLabels.toArray(new String[outLabels.size()])); + } else if (outLabels.isEmpty() && !inLabels.isEmpty()) { + traversal.in(inLabels.toArray(new String[inLabels.size()])); + } else { + traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), __.in(inLabels.toArray(new String[inLabels.size()]))); } + stepIndex++; + this.createContainerQuery(inObj); } @@ -507,12 +535,12 @@ public abstract class GraphTraversalBuilder extends QueryBuilder { /** * Edge query. * - * @param outType the out type - * @param inType the in type + * @param outObj the out type + * @param inObj the in type * @throws NoEdgeRuleFoundException * @throws AAIException */ - private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj) throws AAIException, NoEdgeRuleFoundException { + private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List labels) throws AAIException { String outType = outObj.getDbName(); String inType = inObj.getDbName(); @@ -522,15 +550,37 @@ public abstract class GraphTraversalBuilder extends QueryBuilder { if (inObj.isContainer()) { inType = inObj.getChildDBName(); } + markParentBoundary(); - EdgeRule rule = edgeRules.getEdgeRule(type, outType, inType); - if (rule.getDirection().equals(Direction.OUT)) { - traversal.outE(rule.getLabel()); + Map rules; + if (labels == null) { + rules = edgeRules.getEdgeRules(type, outType, inType); } else { - traversal.inE(rule.getLabel()); + rules = edgeRules.getEdgeRulesWithLabels(type, outType, inType, labels); } - stepIndex++; + final List inLabels = new ArrayList<>(); + final List outLabels = new ArrayList<>(); + + rules.forEach((k, edgeRule) -> { + if (labels != null && !labels.contains(k)) { + return; + } else { + if (edgeRule.getDirection().equals(Direction.IN)) { + inLabels.add(edgeRule.getLabel()); + } else { + outLabels.add(edgeRule.getLabel()); + } + } + }); + + if (inLabels.isEmpty() && !outLabels.isEmpty()) { + traversal.outE(outLabels.toArray(new String[outLabels.size()])); + } else if (outLabels.isEmpty() && !inLabels.isEmpty()) { + traversal.inE(inLabels.toArray(new String[inLabels.size()])); + } else { + traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])), __.inE(inLabels.toArray(new String[inLabels.size()]))); + } } @Override diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java index 62ba5392..f2eaa91f 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java @@ -23,7 +23,6 @@ package org.onap.aai.query.builder; import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -67,7 +66,19 @@ public abstract class GremlinQueryBuilder extends QueryBuilder { */ public GremlinQueryBuilder(Loader loader, GraphTraversalSource source) { super(loader, source); - list = new ArrayList(); + list = new ArrayList<>(); + } + + /** + * Instantiates a new graph gremlin builder. + * + * @param loader the loader + */ + public GremlinQueryBuilder(Loader loader, GraphTraversalSource source, EdgeRules edgeRules) { + super(loader, source); + this.edgeRules = edgeRules; + list = new ArrayList<>(); + } /** @@ -78,43 +89,29 @@ public abstract class GremlinQueryBuilder extends QueryBuilder { */ public GremlinQueryBuilder(Loader loader, GraphTraversalSource source, Vertex start) { super(loader, source, start); - list = new ArrayList(); + list = new ArrayList<>(); } /** - * @{inheritDoc} + * Instantiates a new graph gremlin builder. + * + * @param loader the loader + * @param start the start */ - @Override - public QueryBuilder createDBQuery(Introspector obj) { - this.createKeyQuery(obj); - this.createContainerQuery(obj); - return (QueryBuilder) this; + public GremlinQueryBuilder(Loader loader, GraphTraversalSource source, Vertex start, EdgeRules edgeRules) { + super(loader, source, start); + this.edgeRules = edgeRules; + list = new ArrayList<>(); + } @Override public QueryBuilder exactMatchQuery(Introspector obj) { // TODO not implemented because this is implementation is no longer used this.createKeyQuery(obj); - //allPropertiesQuery(obj); this.createContainerQuery(obj); return (QueryBuilder) this; } - - /** - * @{inheritDoc} - */ - @Override - public QueryBuilder getVerticesByIndexedProperty(String key, Object value) { - return this.getVerticesByProperty(key, value); - } - - /** - * @{inheritDoc} - */ - @Override - public QueryBuilder getVerticesByIndexedProperty(String key, List values) { - return this.getVerticesByProperty(key, values); - } /** * @{inheritDoc} @@ -123,7 +120,7 @@ public abstract class GremlinQueryBuilder extends QueryBuilder { public QueryBuilder getVerticesByProperty(String key, Object value) { String term = ""; - if (value != null && !value.getClass().getName().equals("java.lang.String")) { + if (value != null && !(value instanceof String) ) { term = value.toString(); } else { term = "'" + value + "'"; @@ -143,7 +140,7 @@ public abstract class GremlinQueryBuilder extends QueryBuilder { String predicate = "P.within(#!#argument#!#)"; List arguments = new ArrayList<>(); for (Object item : values) { - if (item != null && !item.getClass().getName().equals("java.lang.String")) { + if (item != null && !(item instanceof String)) { arguments.add(item.toString()); } else { arguments.add("'" + item + "'"); @@ -175,10 +172,10 @@ public abstract class GremlinQueryBuilder extends QueryBuilder { * @{inheritDoc} */ @Override - public QueryBuilder getTypedVerticesByMap(String type, LinkedHashMap map) { + public QueryBuilder getTypedVerticesByMap(String type, Map map) { - for (String key : map.keySet()) { - list.add(".has('" + key + "', '" + map.get(key) + "')"); + for (Map.Entry es : map.entrySet()) { + list.add(".has('" + es.getKey() + "', '" + es.getValue() + "')"); stepIndex++; } list.add(".has('aai-node-type', '" + type + "')"); @@ -207,7 +204,7 @@ public abstract class GremlinQueryBuilder extends QueryBuilder { * @{inheritDoc} */ @Override - public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException, NoEdgeRuleFoundException { + public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException { String parentName = parent.getDbName(); String childName = child.getDbName(); if (parent.isContainer()) { @@ -216,47 +213,73 @@ public abstract class GremlinQueryBuilder extends QueryBuilder { if (child.isContainer()) { childName = child.getChildDBName(); } - this.edgeQueryToVertex(type, parentName, childName); + this.edgeQueryToVertex(type, parentName, childName, null); return this; } - + /** - * @throws NoEdgeRuleFoundException - * @throws AAIException + * * @{inheritDoc} */ @Override - public QueryBuilder createEdgeTraversal(EdgeType type, Vertex parent, Introspector child) throws AAIException, NoEdgeRuleFoundException { - String nodeType = parent.property(AAIProperties.NODE_TYPE).orElse(null); - this.edgeQueryToVertex(type, nodeType, child.getDbName()); - + public QueryBuilder createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in, List labels) throws AAIException { + String parentName = out.getDbName(); + String childName = in.getDbName(); + if (out.isContainer()) { + parentName = out.getChildDBName(); + } + if (in.isContainer()) { + childName = in.getChildDBName(); + } + this.edgeQueryToVertex(type, parentName, childName, labels); return (QueryBuilder) this; - } - - @Override - public QueryBuilder getEdgesBetween(EdgeType type, String outNodeType, String inNodeType) throws AAIException { - this.edgeQuery(type, outNodeType, inNodeType); - - return (QueryBuilder)this; + + public QueryBuilder getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType, List labels) throws AAIException { + this.edgeQuery(type, outNodeType, inNodeType, labels); + return (QueryBuilder)this; } + /** * Edge query. * * @param outType the out type * @param inType the in type - * @throws NoEdgeRuleFoundException - * @throws AAIException + * @throws NoEdgeRuleFoundException + * @throws AAIException */ - private void edgeQueryToVertex(EdgeType type, String outType, String inType) throws AAIException, NoEdgeRuleFoundException { + private void edgeQueryToVertex(EdgeType type, String outType, String inType, List labels) throws AAIException { markParentBoundary(); - EdgeRule rule = edgeRules.getEdgeRule(type, outType, inType); - if (rule.getDirection().equals(Direction.OUT)) { - list.add(".out('" + rule.getLabel() + "')"); + Map rules; + if (labels == null) { + rules = edgeRules.getEdgeRules(type, outType, inType); } else { - list.add(".in('" + rule.getLabel() + "')"); + rules = edgeRules.getEdgeRulesWithLabels(type, outType, inType, labels); + } + + final List inLabels = new ArrayList<>(); + final List outLabels = new ArrayList<>(); + + rules.forEach((k, edgeRule) -> { + if (labels != null && !labels.contains(k)) { + return; + } else { + if (edgeRule.getDirection().equals(Direction.IN)) { + inLabels.add(edgeRule.getLabel()); + } else { + outLabels.add(edgeRule.getLabel()); + } + } + }); + + if (inLabels.isEmpty() && !outLabels.isEmpty()) { + list.add(".out('" + String.join("','", outLabels) + "')"); + } else if (outLabels.isEmpty() && !inLabels.isEmpty()) { + list.add(".in('" + String.join("','", inLabels) + "')"); + } else { + list.add(".union(__.in('" + String.join("','", inLabels) + "')" + ", __.out('" + String.join("','", outLabels) + "'))"); } stepIndex++; list.add(".has('" + AAIProperties.NODE_TYPE + "', '" + inType + "')"); @@ -272,14 +295,38 @@ public abstract class GremlinQueryBuilder extends QueryBuilder { * @throws NoEdgeRuleFoundException * @throws AAIException */ - private void edgeQuery(EdgeType type, String outType, String inType) throws AAIException, NoEdgeRuleFoundException { + private void edgeQuery(EdgeType type, String outType, String inType, List labels) throws AAIException { markParentBoundary(); - EdgeRule rule = edgeRules.getEdgeRule(type, outType, inType); - if (rule.getDirection().equals(Direction.OUT)) { - list.add(".outE('" + rule.getLabel() + "')"); + Map rules; + if (labels == null) { + rules = edgeRules.getEdgeRules(type, outType, inType); } else { - list.add(".inV('" + rule.getLabel() + "')"); + rules = edgeRules.getEdgeRulesWithLabels(type, outType, inType, labels); } + + final List inLabels = new ArrayList<>(); + final List outLabels = new ArrayList<>(); + + rules.forEach((k, edgeRule) -> { + if (labels != null && !labels.contains(k)) { + return; + } else { + if (edgeRule.getDirection().equals(Direction.IN)) { + inLabels.add(edgeRule.getLabel()); + } else { + outLabels.add(edgeRule.getLabel()); + } + } + }); + + if (inLabels.isEmpty() && !outLabels.isEmpty()) { + list.add(".outE('" + String.join("','", outLabels) + "')"); + } else if (outLabels.isEmpty() && !inLabels.isEmpty()) { + list.add(".inE('" + String.join("','", inLabels) + "')"); + } else { + list.add(".union(__.inE('" + String.join("','", inLabels) + "')" + ", __.outE('" + String.join("','", outLabels) + "'))"); + } + stepIndex++; } diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinTraversal.java b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinTraversal.java index 801ac339..b3d3f755 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinTraversal.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinTraversal.java @@ -35,12 +35,23 @@ import org.onap.aai.introspection.Introspector; import org.onap.aai.introspection.Loader; import org.onap.aai.parsers.query.QueryParser; import org.onap.aai.parsers.query.TraversalStrategy; +import org.onap.aai.serialization.db.EdgeRules; /** * The Class GremlinTraversal. */ public class GremlinTraversal extends GremlinQueryBuilder { + /** + * Instantiates a new gremlin traversal. + * + * @param loader the loader + */ + public GremlinTraversal(Loader loader, GraphTraversalSource source, EdgeRules ers) { + super(loader, source, ers); + this.factory = new TraversalStrategy(this.loader, this); + } + /** * Instantiates a new gremlin traversal. * @@ -61,6 +72,17 @@ public class GremlinTraversal extends GremlinQueryBuilder { super(loader, source, start); this.factory = new TraversalStrategy(this.loader, this); } + + /** + * Instantiates a new gremlin traversal. + * + * @param loader the loader + * @param start the start + */ + public GremlinTraversal(Loader loader, GraphTraversalSource source, Vertex start, EdgeRules ers) { + super(loader, source, start, ers); + this.factory = new TraversalStrategy(this.loader, this); + } protected GremlinTraversal(List traversal, Loader loader, GraphTraversalSource source, GremlinQueryBuilder gtb) { super(loader, source); @@ -124,11 +146,15 @@ public class GremlinTraversal extends GremlinQueryBuilder { @Override protected QueryBuilder cloneQueryAtStep(int index) { - if (index == 0) { - index = stepIndex; + + int idx = index; + + if (idx == 0) { + idx = stepIndex; } + List newList = new ArrayList<>(); - for (int i = 0; i < index; i++) { + for (int i = 0; i < idx; i++) { newList.add(this.list.get(i)); } diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinUnique.java b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinUnique.java index 284f8847..85bd7ab4 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinUnique.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinUnique.java @@ -125,11 +125,15 @@ public class GremlinUnique extends GremlinQueryBuilder { @Override protected QueryBuilder cloneQueryAtStep(int index) { - if (index == 0) { - index = stepIndex; + + int idx = index; + + if (idx == 0) { + idx = stepIndex; } + List newList = new ArrayList<>(); - for (int i = 0; i < index; i++) { + for (int i = 0; i < idx; i++) { newList.add(this.list.get(i)); } diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java b/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java index 3a3feafb..5f91a44f 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java @@ -24,35 +24,32 @@ package org.onap.aai.query.builder; import java.io.UnsupportedEncodingException; import java.net.URI; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import javax.ws.rs.core.MultivaluedMap; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.db.props.AAIProperties; import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Introspector; import org.onap.aai.introspection.Loader; import org.onap.aai.parsers.query.QueryParser; import org.onap.aai.parsers.query.QueryParserStrategy; import org.onap.aai.serialization.db.EdgeType; -import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException; /** * The Class QueryBuilder. */ public abstract class QueryBuilder implements Iterator { + protected final GraphTraversalSource source; protected QueryParserStrategy factory = null; - protected Loader loader = null; - protected boolean optimize = false; - protected Vertex start = null; - protected final GraphTraversalSource source; /** * Instantiates a new query builder. @@ -83,7 +80,9 @@ public abstract class QueryBuilder implements Iterator { * @param value the value * @return the vertices by indexed property */ - public abstract QueryBuilder getVerticesByIndexedProperty(String key, Object value); + public QueryBuilder getVerticesByIndexedProperty(String key, Object value) { + return this.getVerticesByProperty(key, value); + } /** * Gets the vertices by property. @@ -100,7 +99,9 @@ public abstract class QueryBuilder implements Iterator { * @param values * @return vertices that match these values */ - public abstract QueryBuilder getVerticesByIndexedProperty(String key, List values); + public QueryBuilder getVerticesByIndexedProperty(String key, List values) { + return this.getVerticesByProperty(key, values); + } /** * filters by all the values for this property @@ -127,7 +128,7 @@ public abstract class QueryBuilder implements Iterator { * @param map the map * @return the typed vertices by map */ - public abstract QueryBuilder getTypedVerticesByMap(String type, LinkedHashMap map); + public abstract QueryBuilder getTypedVerticesByMap(String type, Map map); /** * Creates the DB query. @@ -135,7 +136,11 @@ public abstract class QueryBuilder implements Iterator { * @param obj the obj * @return the query builder */ - public abstract QueryBuilder createDBQuery(Introspector obj); + public QueryBuilder createDBQuery(Introspector obj) { + this.createKeyQuery(obj); + this.createContainerQuery(obj); + return (QueryBuilder) this; + } /** * Creates the key query. @@ -169,16 +174,79 @@ public abstract class QueryBuilder implements Iterator { * @param child the child * @return the query builder */ - public abstract QueryBuilder createEdgeTraversal(EdgeType type, Vertex parent, Introspector child) throws AAIException; + public QueryBuilder createEdgeTraversal(EdgeType type, Vertex parent, Introspector child) throws AAIException { + String nodeType = parent.property(AAIProperties.NODE_TYPE).orElse(null); + this.createEdgeTraversal(type, nodeType, child.getDbName()); + return (QueryBuilder) this; + + } - public QueryBuilder createEdgeTraversal(EdgeType type, String outNodeType, String inNodeType) throws NoEdgeRuleFoundException, AAIException { + /** + * + * @param type + * @param outNodeType + * @param inNodeType + * @return + * @throws AAIException + */ + public QueryBuilder createEdgeTraversal(EdgeType type, String outNodeType, String inNodeType) throws AAIException { Introspector out = loader.introspectorFromName(outNodeType); Introspector in = loader.introspectorFromName(inNodeType); - + return createEdgeTraversal(type, out, in); } - - public abstract QueryBuilder getEdgesBetween(EdgeType type, String outNodeType, String inNodeType) throws AAIException; + + /** + * + * @param type + * @param outNodeType + * @param inNodeType + * @param labels + * @return + * @throws AAIException + */ + public QueryBuilder createEdgeTraversalWithLabels(EdgeType type, String outNodeType, String inNodeType, List labels) throws AAIException { + Introspector out = loader.introspectorFromName(outNodeType); + Introspector in = loader.introspectorFromName(inNodeType); + + return createEdgeTraversalWithLabels(type, out, in, labels); + } + + /** + * + * @param type + * @param out + * @param in + * @param labels + * @return + */ + public abstract QueryBuilder createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in, List labels) throws AAIException; + + /** + * + * @param type + * @param outNodeType + * @param inNodeType + * @return + * @throws AAIException + */ + public QueryBuilder getEdgesBetween(EdgeType type, String outNodeType, String inNodeType) throws AAIException { + this.getEdgesBetweenWithLabels(type, outNodeType, inNodeType, null); + + return (QueryBuilder)this; + + } + /** + * + * @param type + * @param outNodeType + * @param inNodeType + * @param labels + * @return + * @throws AAIException + */ + public abstract QueryBuilder getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType, List labels) throws AAIException; + /** * Creates the query from URI. * @@ -238,6 +306,7 @@ public abstract class QueryBuilder implements Iterator { public abstract void markParentBoundary(); public abstract QueryBuilder limit(long amount); + /** * New instance. * diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/TraversalQuery.java b/aai-core/src/main/java/org/onap/aai/query/builder/TraversalQuery.java index 60e72aa5..014ce79d 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/TraversalQuery.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/TraversalQuery.java @@ -31,12 +31,12 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.Vertex; - import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Introspector; import org.onap.aai.introspection.Loader; import org.onap.aai.parsers.query.QueryParser; import org.onap.aai.parsers.query.TraversalStrategy; +import org.onap.aai.serialization.db.EdgeRules; /** * The Class TraversalQuery. @@ -53,6 +53,19 @@ public class TraversalQuery extends GraphTraversalBuilder { this.factory = new TraversalStrategy(this.loader, this); } + /** + * Instantiates a new traversal query. + * + * @param loader the loader + * @param source graph traversal source + * @param edgeRules the edgeRules to use + */ + public TraversalQuery(Loader loader, GraphTraversalSource source, EdgeRules edgeRules) { + super(loader, source, edgeRules); + this.factory = new TraversalStrategy(this.loader, this); + + } + /** * Instantiates a new traversal query. * @@ -64,6 +77,17 @@ public class TraversalQuery extends GraphTraversalBuilder { this.factory = new TraversalStrategy(this.loader, this); } + /** + * Instantiates a new traversal query. + * + * @param loader the loader + * @param start the start + */ + public TraversalQuery(Loader loader, GraphTraversalSource source, Vertex start, EdgeRules edgeRules) { + super(loader, source, start, edgeRules); + this.factory = new TraversalStrategy(this.loader, this); + } + protected TraversalQuery(GraphTraversal traversal, Loader loader, GraphTraversalSource source, GraphTraversalBuilder gtb) { super(loader, source); this.traversal = traversal; @@ -125,14 +149,17 @@ public class TraversalQuery extends GraphTraversalBuilder { @Override protected QueryBuilder cloneQueryAtStep(int index) { - if (index == 0) { - index = stepIndex; + int idx = index; + + if (idx == 0) { + idx = stepIndex; } + GraphTraversal clone = this.traversal.asAdmin().clone(); GraphTraversal.Admin cloneAdmin = clone.asAdmin(); List steps = cloneAdmin.getSteps(); - for (int i = steps.size()-1; i >= index; i--) { + for (int i = steps.size()-1; i >= idx; i--) { cloneAdmin.removeStep(i); } return new TraversalQuery<>(cloneAdmin, loader, source, this); diff --git a/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java b/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java index 634e44e9..0d89a74c 100644 --- a/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java +++ b/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java @@ -461,14 +461,13 @@ public class HttpEntry { private Introspector getObjectFromDb(List results, DBSerializer serializer, QueryParser query, Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp) throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIUnknownObjectException, URISyntaxException { //nothing found - if (results.size() == 0) { + if (results.isEmpty()) { String msg = createNotFoundMessage(query.getResultType(), uri); throw new AAIException("AAI_6114", msg); } - obj = serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp); + return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp); - return obj; } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java b/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java index dd073dc7..cd48fc6a 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java @@ -1,40 +1,67 @@ -/** +/*- * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ + package org.onap.aai.serialization.db; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; -import com.google.common.base.CaseFormat; -import com.thinkaurelius.titan.core.SchemaViolationException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import javax.ws.rs.core.UriBuilder; + import org.apache.commons.collections.IteratorUtils; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; -import org.apache.tinkerpop.gremlin.structure.*; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.javatuples.Pair; +import org.javatuples.Triplet; import org.onap.aai.db.props.AAIProperties; import org.onap.aai.exceptions.AAIException; -import org.onap.aai.introspection.*; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.IntrospectorFactory; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.LoaderFactory; +import org.onap.aai.introspection.ModelType; +import org.onap.aai.introspection.PropertyPredicates; +import org.onap.aai.introspection.Version; import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; import org.onap.aai.introspection.sideeffect.DataCopy; import org.onap.aai.introspection.sideeffect.DataLinkReader; @@ -48,6 +75,7 @@ import org.onap.aai.query.builder.QueryBuilder; import org.onap.aai.schema.enums.ObjectMetadata; import org.onap.aai.schema.enums.PropertyMetadata; import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException; +import org.onap.aai.serialization.db.util.VersionChecker; import org.onap.aai.serialization.engines.TransactionalGraphEngine; import org.onap.aai.serialization.tinkerpop.TreeBackedVertex; import org.onap.aai.util.AAIApiServerURLBase; @@ -55,18 +83,10 @@ import org.onap.aai.util.AAIConfig; import org.onap.aai.util.AAIConstants; import org.onap.aai.workarounds.NamingExceptions; -import javax.ws.rs.core.UriBuilder; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Array; -import java.lang.reflect.InvocationTargetException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.*; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.google.common.base.CaseFormat; +import com.thinkaurelius.titan.core.SchemaViolationException; public class DBSerializer { @@ -109,8 +129,7 @@ public class DBSerializer { */ public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) { - long unixTimeNow = System.currentTimeMillis(); - String timeNowInSec = "" + unixTimeNow; + String timeNowInSec = Long.toString(System.currentTimeMillis()); if (isNewVertex) { v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth); v.property(AAIProperties.CREATED_TS, timeNowInSec); @@ -391,15 +410,20 @@ public class DBSerializer { List relationships = (List)wrapped.getValue("relationship"); - List> addEdges = new ArrayList<>(); + List> addEdges = new ArrayList<>(); List existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader()); for (Object relationship : relationships) { Edge e = null; Vertex cousinVertex = null; + String label = null; Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship); QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel); + if (wrappedRel.hasProperty("relationship-label")) { + label = wrappedRel.getValue("relationship-label"); + } + List results = parser.getQueryBuilder().toList(); if (results.isEmpty()) { final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); @@ -412,30 +436,30 @@ public class DBSerializer { } if (cousinVertex != null) { - try { - if (!edgeRules.hasEdgeRule(v, cousinVertex)) { - throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", " - + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + "."); - } - e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex); - - if (e == null) { - addEdges.add(new Pair<>(v, cousinVertex)); - } else { - existingEdges.remove(e); - } - } catch (NoEdgeRuleFoundException e1) { + + if (!edgeRules.hasEdgeRule(v, cousinVertex, label)) { + throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", " + + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + (label != null ? (" with label " + label):"") +"."); + } else if (edgeRules.hasTreeEdgeRule(v, cousinVertex) && !edgeRules.hasCousinEdgeRule(v, cousinVertex, label)) { throw new AAIException("AAI_6145"); } + + e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label); + + if (e == null) { + addEdges.add(new Triplet<>(v, cousinVertex, label)); + } else { + existingEdges.remove(e); + } } } for (Edge edge : existingEdges) { edge.remove(); } - for (Pair pair : addEdges) { + for (Triplet triplet : addEdges) { try { - edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), pair.getValue0(), pair.getValue1()); + edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), triplet.getValue2()); } catch (NoEdgeRuleFoundException e) { throw new AAIException("AAI_6129", e); } @@ -491,7 +515,7 @@ public class DBSerializer { //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI()); //List items = p.getQuery().toList(); - QueryBuilder query = this.engine.getQueryBuilder(v); + QueryBuilder query = this.engine.getQueryBuilder(v); query.createEdgeTraversal(EdgeType.TREE, v, dependentObj); query.createKeyQuery(dependentObj); @@ -531,14 +555,13 @@ public class DBSerializer { String parentUri = parent.property(AAIProperties.AAI_URI).orElse(null); if (parentUri != null) { - String uri; - uri = obj.getURI(); + String uri = obj.getURI(); child.property(AAIProperties.AAI_URI, parentUri + uri); } processObject(obj, child, requestContext); Edge e; - e = this.getEdgeBetween(EdgeType.TREE, parent, child); + e = this.getEdgeBetween(EdgeType.TREE, parent, child, null); if (e == null) { String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED); if (canBeLinked != null && canBeLinked.equals("true")) { @@ -617,9 +640,7 @@ public class DBSerializer { for (Future future : futures) { try { getList.add(future.get()); - } catch (ExecutionException e) { - throw new AAIException("AAI_4000", e); - } catch (InterruptedException e) { + } catch (ExecutionException | InterruptedException e) { throw new AAIException("AAI_4000", e); } } @@ -726,7 +747,7 @@ public class DBSerializer { Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp); - if (result != null && getList != null) { + if (result != null) { getList.add(argumentObject.getUnderlyingObject()); } @@ -868,19 +889,28 @@ public class DBSerializer { */ private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException { - List cousins = this.engine.getQueryEngine().findCousinVertices(v); List relationshipObjList = obj.getValue("relationship"); for (Vertex cousin : cousins) { - + if (VersionChecker.apiVersionNeedsEdgeLabel(obj.getVersion())) { + List edges = this.getEdgesBetween(EdgeType.COUSIN, v, cousin); + for (Edge e : edges) { + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, e); + if (result != null) { + relationshipObjList.add(result); + } + } + } else { Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); - Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null); if (result != null) { relationshipObjList.add(result); } - + } + } if (relationshipObjList.isEmpty()) { @@ -910,7 +940,7 @@ public class DBSerializer { * @throws AAIUnknownObjectException * @throws URISyntaxException */ - private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp) throws UnsupportedEncodingException, AAIUnknownObjectException { + private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, Edge edge) throws UnsupportedEncodingException, AAIUnknownObjectException { //we must look up all parents in this case because we need to compute name-properties @@ -935,11 +965,14 @@ public class DBSerializer { } return null; } - - if(list.size() > 0 && this.version.compareTo(Version.v8) >= 0){ + if (!list.isEmpty()) { this.addRelatedToProperty(result, list.get(0)); } - + + if (edge != null && result.hasProperty("relationship-label")) { + result.setValue("relationship-label", edge.label()); + } + return result.getUnderlyingObject(); } @@ -993,8 +1026,7 @@ public class DBSerializer { } uri = sb.toString(); - URI result = UriBuilder.fromPath(uri).build(); - return result; + return UriBuilder.fromPath(uri).build(); } /** @@ -1063,19 +1095,19 @@ public class DBSerializer { */ public void setCachedURIs(List vertices, List objs) throws UnsupportedEncodingException, URISyntaxException { - String uriChain = ""; + StringBuilder uriChain = new StringBuilder(); for (int i = 0; i < vertices.size(); i++) { String aaiUri = ""; Vertex v = null; v = vertices.get(i); aaiUri = v.property(AAIProperties.AAI_URI).orElse(null); if (aaiUri != null) { - uriChain += aaiUri; + uriChain.append(aaiUri); } else { URI uri = UriBuilder.fromPath(objs.get(i).getURI()).build(); aaiUri = uri.toString(); - uriChain += aaiUri; - v.property(AAIProperties.AAI_URI, uriChain); + uriChain.append(aaiUri); + v.property(AAIProperties.AAI_URI, uriChain.toString()); } } @@ -1106,7 +1138,7 @@ public class DBSerializer { } } - if (relatedToProperties.size() > 0) { + if (!relatedToProperties.isEmpty()) { List relatedToList = (List)relationship.getValue("related-to-property"); for (Introspector obj : relatedToProperties) { relatedToList.add(obj.getUnderlyingObject()); @@ -1130,8 +1162,13 @@ public class DBSerializer { QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); + String label = null; + if (relationship.hasProperty("relationship-label")) { + label = relationship.getValue("relationship-label"); + } + List results = parser.getQueryBuilder().toList(); - if (results.size() == 0) { + if (results.isEmpty()) { AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); e.getTemplateVars().add(parser.getResultType()); e.getTemplateVars().add(parser.getUri().toString()); @@ -1143,29 +1180,19 @@ public class DBSerializer { if (relatedVertex != null) { - Edge e; - try { - e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex); - if (e == null) { - edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex); - - } else { - //attempted to link two vertexes already linked - } - } catch (NoEdgeRuleFoundException e1) { - throw new AAIException("AAI_6129", e1); + Edge e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); + if (e == null) { + edgeRules.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label); + } else { + //attempted to link two vertexes already linked } - - - - } return true; } /** - * Gets the edges between. + * Gets all the edges between of the type. * * @param aVertex the out vertex * @param bVertex the in vertex @@ -1173,44 +1200,67 @@ public class DBSerializer { * @throws AAIException the AAI exception * @throws NoEdgeRuleFoundException */ - private List getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException, NoEdgeRuleFoundException { + private List getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) { List result = new ArrayList<>(); if (bVertex != null) { - EdgeRule rule = edgeRules.getEdgeRule(type, aVertex, bVertex); - GraphTraversal findEdgesBetween = null; - findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(rule.getLabel()).filter(__.otherV().hasId(bVertex.id())); - List edges = findEdgesBetween.toList(); - for (Edge edge : edges) { - if (edge.label().equals(rule.getLabel())) { - result.add(edge); - } + GraphTraversal findEdgesBetween = null; + findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(); + if (EdgeType.TREE.equals(type)) { + findEdgesBetween = findEdgesBetween.not(__.has(EdgeProperty.CONTAINS.toString(), "NONE")); + } else { + findEdgesBetween = findEdgesBetween.has(EdgeProperty.CONTAINS.toString(), "NONE"); + } + findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())); + result = findEdgesBetween.toList(); + } + + return result; + } + /** + * Gets all the edges between the vertexes with the label and type. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @param label + * @return the edges between + * @throws AAIException the AAI exception + */ + private List getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException { + + List result = new ArrayList<>(); + + if (bVertex != null) { + EdgeRule rule = edgeRules.getEdgeRule(type, aVertex, bVertex, label); + List edges = this.getEdgesBetween(type, aVertex, bVertex); + for (Edge edge : edges) { + if (edge.label().equals(rule.getLabel())) { + result.add(edge); } - + } } return result; } /** - * Gets the edge between. + * Gets the edge between with the label and edge type. * * @param aVertex the out vertex * @param bVertex the in vertex + * @param label * @return the edge between * @throws AAIException the AAI exception * @throws NoEdgeRuleFoundException */ - public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException { - - - + public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException { + if (bVertex != null) { - List edges = this.getEdgesBetween(type, aVertex, bVertex); + List edges = this.getEdgesBetween(type, aVertex, bVertex, label); - if (edges.size() > 0) { + if (!edges.isEmpty()) { return edges.get(0); } @@ -1218,6 +1268,9 @@ public class DBSerializer { return null; } + public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException { + return this.getEdgeBetween(type, aVertex, bVertex, null); + } /** @@ -1236,15 +1289,20 @@ public class DBSerializer { QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); List results = parser.getQueryBuilder().toList(); + + String label = null; + if (relationship.hasProperty("relationship-label")) { + label = relationship.getValue("relationship-label"); + } - if (results.size() == 0) { + if (results.isEmpty()) { return false; } relatedVertex = results.get(0); Edge edge; try { - edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex); + edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); } catch (NoEdgeRuleFoundException e) { throw new AAIException("AAI_6129", e); } @@ -1329,7 +1387,7 @@ public class DBSerializer { } List preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertex).union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE), __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE)).dedup().toList(); - if (preventDeleteVertices.size() > 0) { + if (!preventDeleteVertices.isEmpty()) { aaiExceptionCode = "AAI_6110"; errorDetail = String.format("Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s", preventDeleteVertices); result = false; @@ -1368,7 +1426,7 @@ public class DBSerializer { ErrorLogHelper.logException(e); } // We're only doing the resource version checks for v5 and later - if (enabled.equals("true") && this.version.compareTo(Version.v8) > 0) { + if (enabled.equals("true")) { if (!currentResourceVersion.equals(resourceVersion)) { if (action.equals("create") && !resourceVersion.equals("")) { errorDetail = "resource-version passed for " + action + " of " + uri; diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeProperty.java b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeProperty.java index 29d82298..ecd749c7 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeProperty.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeProperty.java @@ -1,37 +1,30 @@ -/** +/*- * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ + package org.onap.aai.serialization.db; public enum EdgeProperty { - FROM("from"), - TO("to"), - LABEL("label"), - DIRECTION("direction"), - MULTIPLICITY("multiplicity"), CONTAINS("contains-other-v"), DELETE_OTHER_V("delete-other-v"), SVC_INFRA("SVC-INFRA"), PREVENT_DELETE("prevent-delete"); - private final String name; private EdgeProperty(String name) { diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRule.java b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRule.java index 309dbffe..ae9e96c0 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRule.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRule.java @@ -1,43 +1,43 @@ -/** +/*- * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ -package org.onap.aai.serialization.db; -import org.apache.tinkerpop.gremlin.structure.Direction; +package org.onap.aai.serialization.db; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Map; +import org.apache.tinkerpop.gremlin.structure.Direction; + public class EdgeRule { private String label = ""; private MultiplicityRule multiplicityRule = null; private Direction direction = null; private Map edgeProperties = null; + private boolean isDefaultEdge = false; /** * Instantiates a new edge rule. */ public EdgeRule() { - edgeProperties = new HashMap<>(); + edgeProperties = new EnumMap<>(EdgeProperty.class); } /** @@ -206,6 +206,18 @@ public class EdgeRule { private String getProp(EdgeProperty key) { return this.edgeProperties.get(key); } + + public boolean isDefault() { + return isDefaultEdge; + } + + public void setIsDefault(boolean isDefault) { + this.isDefaultEdge = isDefault; + } + + public void setIsDefault(String isDefault) { + this.isDefaultEdge = "true".equals(isDefault); + } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRules.java b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRules.java index d9dfa457..349f9e6b 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRules.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/EdgeRules.java @@ -44,6 +44,7 @@ import org.onap.aai.db.props.AAIProperties; import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Version; import org.onap.aai.serialization.db.exceptions.EdgeMultiplicityException; +import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException; import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException; import com.att.eelf.configuration.EELFLogger; @@ -56,6 +57,12 @@ import com.jayway.jsonpath.JsonPath; public class EdgeRules { + private static final String LABEL = "label"; + + private static final String NOT_DIRECTION_NOTATION = "!${direction}"; + + private static final String DIRECTION_NOTATION = "${direction}"; + private EELFLogger logger = EELFManager.getInstance().getLogger(EdgeRules.class); private DocumentContext rulesDoc; @@ -77,54 +84,14 @@ public class EdgeRules { rulesDoc = JsonPath.parse(json); } - private String getEdgeRuleJson(String rulesFilename) { - InputStream is = getClass().getResourceAsStream(rulesFilename); - - Scanner scanner = new Scanner(is); - String json = scanner.useDelimiter("\\Z").next(); - scanner.close(); - - return json; - } - /** * Loads the versioned DbEdgeRules json file for later parsing. */ - @SuppressWarnings("unchecked") private EdgeRules(Version version) { String json = this.getEdgeRuleJson(version); rulesDoc = JsonPath.parse(json); } - - private String getEdgeRuleJson(Version version) { - InputStream is = getClass().getResourceAsStream("/dbedgerules/DbEdgeRules_" + version.toString() + ".json"); - Scanner scanner = new Scanner(is); - String json = scanner.useDelimiter("\\Z").next(); - scanner.close(); - - return json; - } - - private static class Helper { - private static final EdgeRules INSTANCE = new EdgeRules(); - private static final Map INSTANCEMAP = new ConcurrentHashMap<>(); - - private static EdgeRules getEdgeRulesByFilename(String rulesFilename) { - return new EdgeRules(rulesFilename); - } - - private static EdgeRules getVersionedEdgeRules(Version v) { - if (Version.isLatest(v)) { - return INSTANCE; - } - if (!INSTANCEMAP.containsKey(v)) { - INSTANCEMAP.put(v, new EdgeRules(v)); - } - return INSTANCEMAP.get(v); - } - } - /** * Gets the single instance of EdgeRules. * @@ -154,7 +121,21 @@ public class EdgeRules { public static EdgeRules getInstance(String rulesFilename) { return Helper.getEdgeRulesByFilename(rulesFilename); } + + private String getEdgeRuleJson(String rulesFilename) { + InputStream is = getClass().getResourceAsStream(rulesFilename); + Scanner scanner = new Scanner(is); + String json = scanner.useDelimiter("\\Z").next(); + scanner.close(); + + return json; + } + + private String getEdgeRuleJson(Version version) { + return this.getEdgeRuleJson("/dbedgerules/DbEdgeRules_" + version.toString() + ".json"); + } + /** * Adds the tree edge. * @@ -164,9 +145,9 @@ public class EdgeRules { * @throws AAIException the AAI exception */ public Edge addTreeEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException { - return this.addEdge(EdgeType.TREE, traversalSource, aVertex, bVertex, false); + return this.addEdge(EdgeType.TREE, traversalSource, aVertex, bVertex, false, null); } - + /** * Adds the edge. * @@ -176,9 +157,13 @@ public class EdgeRules { * @throws AAIException the AAI exception */ public Edge addEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException { - return this.addEdge(EdgeType.COUSIN, traversalSource, aVertex, bVertex, false); + return this.addEdge(traversalSource, aVertex, bVertex, null); } - + + public Edge addEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex, String label) throws AAIException { + return this.addEdge(EdgeType.COUSIN, traversalSource, aVertex, bVertex, false, label); + } + /** * Adds the tree edge. * @@ -188,9 +173,9 @@ public class EdgeRules { * @throws AAIException the AAI exception */ public Edge addTreeEdgeIfPossible(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException { - return this.addEdge(EdgeType.TREE, traversalSource, aVertex, bVertex, true); + return this.addEdge(EdgeType.TREE, traversalSource, aVertex, bVertex, true, null); } - + /** * Adds the edge. * @@ -200,9 +185,13 @@ public class EdgeRules { * @throws AAIException the AAI exception */ public Edge addEdgeIfPossible(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException { - return this.addEdge(EdgeType.COUSIN, traversalSource, aVertex, bVertex, true); + return this.addEdgeIfPossible(traversalSource, aVertex, bVertex, null); } + public Edge addEdgeIfPossible(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex, String label) throws AAIException { + return this.addEdge(EdgeType.COUSIN, traversalSource, aVertex, bVertex, true, label); + } + /** * Adds the edge. * @@ -212,14 +201,14 @@ public class EdgeRules { * @return the edge * @throws AAIException the AAI exception */ - private Edge addEdge(EdgeType type, GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex, boolean isBestEffort) throws AAIException { + private Edge addEdge(EdgeType type, GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex, boolean isBestEffort, String label) throws AAIException { - EdgeRule rule = this.getEdgeRule(type, aVertex, bVertex); + EdgeRule rule = this.getEdgeRule(type, aVertex, bVertex, label); Edge e = null; - + Optional message = this.validateMultiplicity(rule, traversalSource, aVertex, bVertex); - + if (message.isPresent() && !isBestEffort) { throw new EdgeMultiplicityException(message.get()); } @@ -229,12 +218,12 @@ public class EdgeRules { } else if (rule.getDirection().equals(Direction.IN)) { e = bVertex.addEdge(rule.getLabel(), aVertex); } - + this.addProperties(e, rule); } return e; } - + /** * Adds the properties. * @@ -242,40 +231,91 @@ public class EdgeRules { * @param rule the rule */ public void addProperties(Edge edge, EdgeRule rule) { - + // In DbEdgeRules.EdgeRules -- What we have as "edgeRule" is a comma-delimited set of strings. // The first item is the edgeLabel. // The second in the list is always "direction" which is always OUT for the way we've implemented it. - // Items starting at "firstTagIndex" and up are all assumed to be booleans that map according to + // Items starting at "firstTagIndex" and up are all assumed to be booleans that map according to // tags as defined in EdgeInfoMap. // Note - if they are tagged as 'reverse', that means they get the tag name with "-REV" on it Map propMap = rule.getEdgeProperties(); - + for (Entry entry : propMap.entrySet()) { edge.property(entry.getKey().toString(), entry.getValue()); } } - + /** - * Checks if any edge rules exist between the two given nodes, in either A|B or B|A order. + * Checks if any edge rules exist between the two given node types, in either A|B or B|A order. * * @param nodeA - node at one end of the edge * @param nodeB - node at the other end * @return true, if any such rules exist */ public boolean hasEdgeRule(String nodeA, String nodeB) { - Filter aToB = filter( - where("from").is(nodeA).and("to").is(nodeB) - ); - Filter bToA = filter( - where("from").is(nodeB).and("to").is(nodeA) - ); - - List> results = readRules(aToB); - results.addAll(readRules(bToA)); + return this.hasEdgeRule(nodeA, nodeB, null); + } + + /** + * Checks if any edge rules exist between the two given node types with contains-other-v !NONE, in either A|B or B|A order. + * + * @param nodeA - node at one end of the edge + * @param nodeB - node at the other end + * @return true, if any such rules exist + */ + public boolean hasTreeEdgeRule(String nodeA, String nodeB) { + return this.hasEdgeRule(EdgeType.TREE, nodeA, nodeB, null); + } - return !results.isEmpty(); - + /** + * Checks if any edge rules exist between the two given node types with contains-other-v NONE, in either A|B or B|A order. + * + * @param nodeA - node at one end of the edge + * @param nodeB - node at the other end + * @param label - edge label + * @return true, if any such rules exist + */ + public boolean hasCousinEdgeRule(String nodeA, String nodeB, String label) { + return this.hasEdgeRule(EdgeType.COUSIN, nodeA, nodeB, label); + } + + /** + * Checks if any edge rules exist between the two given nodes with contains-other-v !NONE, in either A|B or B|A order. + * + * @param aVertex - node at one end of the edge + * @param bVertex - node at the other end + * @return true, if any such rules exist + */ + public boolean hasTreeEdgeRule(Vertex aVertex, Vertex bVertex) { + String outType = aVertex.property(AAIProperties.NODE_TYPE).orElse(null); + String inType = bVertex.property(AAIProperties.NODE_TYPE).orElse(null); + return this.hasTreeEdgeRule(outType, inType); + } + + /** + * Checks if any edge rules exist between the two given nodes with contains-other-v NONE with edge label, in either A|B or B|A order. + * + * @param aVertex - node at one end of the edge + * @param bVertex - node at the other end + * @param label - edge label + * @return true, if any such rules exist + */ + public boolean hasCousinEdgeRule(Vertex aVertex, Vertex bVertex, String label) { + String outType = aVertex.property(AAIProperties.NODE_TYPE).orElse(null); + String inType = bVertex.property(AAIProperties.NODE_TYPE).orElse(null); + return this.hasCousinEdgeRule(outType, inType, label); + } + + /** + * Checks if any edge rules exist between the two given nodes w/ edge label, in either A|B or B|A order. + * + * @param nodeA - node at one end of the edge + * @param nodeB - node at the other end + * @param label - edge label + * @return true, if any such rules exist + */ + public boolean hasEdgeRule(String nodeA, String nodeB, String label) { + return this.hasEdgeRule(null, nodeA, nodeB, label); } /** @@ -286,11 +326,65 @@ public class EdgeRules { * @return true, if any such rules exist */ public boolean hasEdgeRule(Vertex aVertex, Vertex bVertex) { - String outType = aVertex.property("aai-node-type").orElse(null); - String inType = bVertex.property("aai-node-type").orElse(null); - - return this.hasEdgeRule(outType, inType); - + return this.hasEdgeRule(aVertex, bVertex, null); + + } + + /** + * Checks if any edge rules exist between the two given nodes with label, in either A|B or B|A order with edge label. + * + * @param aVertex - node at one end of the edge + * @param bVertex - node at the other end + * @param label - edge label + * @return true, if any such rules exist + */ + public boolean hasEdgeRule(Vertex aVertex, Vertex bVertex, String label) { + String outType = aVertex.property(AAIProperties.NODE_TYPE).orElse(null); + String inType = bVertex.property(AAIProperties.NODE_TYPE).orElse(null); + + if (label == null) { + return this.hasEdgeRule(outType, inType); + } else { + return this.hasEdgeRule(outType, inType, label); + } + } + + /** + * Checks if any edge rules exist between the two given node types, in either A|B or B|A order with edge label and edge type. + * + * @param type - type of edge EdgeType.COUSIN | EdgeType.TREE + * @param nodeA - node at one end of the edge + * @param nodeB - node at the other end + * @param label - edge label + * @return true, if any such rules exist + */ + public boolean hasEdgeRule(EdgeType type, String nodeA, String nodeB, String label) { + Filter aToB = filter( + where("from").is(nodeA) + .and("to").is(nodeB) + ); + Filter bToA = filter( + where("from").is(nodeB) + .and("to").is(nodeA) + ); + + if (EdgeType.TREE.equals(type)) { + aToB = aToB.and(where(EdgeProperty.CONTAINS.toString()).ne(AAIDirection.NONE.toString())); + bToA = bToA.and(where(EdgeProperty.CONTAINS.toString()).ne(AAIDirection.NONE.toString())); + } else if (EdgeType.COUSIN.equals(type)) { + aToB = aToB.and(where(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString())); + bToA = bToA.and(where(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString())); + } + + if (label != null) { + aToB = aToB.and(where(LABEL).is(label)); + bToA = bToA.and(where(LABEL).is(label)); + } + + List results = rulesDoc.read("$.rules.[?]", aToB); + results.addAll(rulesDoc.read("$.rules.[?]", bToA)); + + return !results.isEmpty(); } /** @@ -298,29 +392,109 @@ public class EdgeRules { * The rules will be phrased in terms of out|in, though this will * also find rules defined as in|out (it will flip the direction in * the EdgeRule object returned accordingly to match out|in). - * - * @param outType + * + * @param outType + * @param inType + * @return Map where edgeLabel is the label name + * @throws AAIException + */ + public Map getEdgeRules(String outType, String inType) { + return this.getEdgeRules(outType, inType, null); + } + + /** + * Gets all the edge rules that exist between the given node types with given label. + * The rules will be phrased in terms of out|in, though this will + * also find rules defined as in|out (it will flip the direction in + * the EdgeRule object returned accordingly to match out|in). + * + * @param outType * @param inType + * @param label * @return Map where edgeLabel is the label name * @throws AAIException */ - public Map getEdgeRules(String outType, String inType) throws AAIException { - Map result = new HashMap<>(); - EdgeRule rule = null; + public Map getEdgeRules(String outType, String inType, String label) { + final Map result = new HashMap<>(); + for (EdgeType type : EdgeType.values()) { - try { - rule = this.getEdgeRule(type, outType, inType); - result.put(rule.getLabel(), rule); - } catch (NoEdgeRuleFoundException e) { - continue; - } + result.putAll(this.getEdgeRules(type, outType, inType, label)); } - + return result; } - + /** + * Looks up edge rules for the given node types and the labels specified + * @param type + * @param outType + * @param inType + * @param labels + * @return + * @throws NoEdgeRuleFoundException + * @throws MultipleEdgeRuleFoundException + */ + public Map getEdgeRulesWithLabels(EdgeType type, String outType, String inType, List labels) throws NoEdgeRuleFoundException, MultipleEdgeRuleFoundException { + final Map result = new HashMap<>(); + + if (labels == null || labels.isEmpty()) { + throw new NoEdgeRuleFoundException("No labels specified"); + } + for (String label : labels) { + EdgeRule er = this.getEdgeRule(type, outType, inType, label); + result.put(er.getLabel(), er); + } + + return result; + } + /** + * Gets all the edge rules of that edge type that exist between the given node types with given label. + * The rules will be phrased in terms of out|in, though this will + * also find rules defined as in|out (it will flip the direction in + * the EdgeRule object returned accordingly to match out|in). + * + * @param type + * @param outType + * @param inType + * @param label + * @return + * @throws AAIException + */ + public Map getEdgeRules(EdgeType type, String outType, String inType, String label) { + final Map result = new HashMap<>(); + + this.getEdgeRulesFromJson(type, outType, inType, label).forEach(edgeRuleJson -> { + EdgeRule edgeRule = this.buildRule(edgeRuleJson); + result.put(edgeRule.getLabel(), edgeRule); + }); + this.getEdgeRulesFromJson(type, inType, outType, label).forEach(erj -> { + EdgeRule edgeRule = this.flipDirection(this.buildRule(erj)); + if (!result.containsKey(edgeRule.getLabel())) { + result.put(edgeRule.getLabel(), edgeRule); + } + }); + + + return result; + } + + /** + * Gets all the edge rules of that edge type that exist between the given node types. + * The rules will be phrased in terms of out|in, though this will + * also find rules defined as in|out (it will flip the direction in + * the EdgeRule object returned accordingly to match out|in). + * + * @param type + * @param outType + * @param inType + * @return + * @throws AAIException + */ + public Map getEdgeRules(EdgeType type, String outType, String inType) { + return this.getEdgeRules(type, outType, inType, null); + } + /** * Gets the edge rule of the given type that exists between A and B. * Will check B|A as well, and flips the direction accordingly if that succeeds @@ -333,69 +507,155 @@ public class EdgeRules { * @throws AAIException if no such edge exists */ public EdgeRule getEdgeRule(EdgeType type, String nodeA, String nodeB) throws AAIException { - //try A to B - List> aToBEdges = readRules(buildFilter(type, nodeA, nodeB)); - if (!aToBEdges.isEmpty()) { - //lazily stop iterating if we find a match - //should there be a mismatch between type and isParent, - //the caller will receive something. - //this operates on the assumption that there are at most two rules - //for a given vertex pair - verifyRule(aToBEdges.get(0)); - return buildRule(aToBEdges.get(0)); - } - - //we get here if there was nothing for A to B, so let's try B to A - List> bToAEdges = readRules(buildFilter(type, nodeB, nodeA)); - if (!bToAEdges.isEmpty()) { - verifyRule(bToAEdges.get(0)); - return flipDirection(buildRule(bToAEdges.get(0))); //bc we need to return as A|B, so flip the direction to match + return this.getEdgeRule(type, nodeA, nodeB, null); + } + + /** + * Gets the edge rule of the given type that exists between A and B with edge label. + * Will check B|A as well, and flips the direction accordingly if that succeeds + * to match the expected A|B return. + * + * @param type - the type of edge you're looking for + * @param nodeA - first node type + * @param nodeB - second node type + * @param label - edge label + * @return EdgeRule describing the rule in terms of A|B, if there is any such rule + * @throws MultipleEdgeRuleFoundException + * @throws AAIException if no such edge exists + */ + public EdgeRule getEdgeRule(EdgeType type, String nodeA, String nodeB, String label) throws NoEdgeRuleFoundException, MultipleEdgeRuleFoundException { + + final StringBuilder errorMsg = new StringBuilder(); + errorMsg.append(type.toString()) + .append(" edge rule between ") + .append(nodeA).append(" and ").append(nodeB); + if (label != null) { + errorMsg.append(" with label ").append(label); } - + + EdgeRule edgeRule; + Map edgeRules = this.getEdgeRules(type, nodeA, nodeB, label); + //found none - throw new NoEdgeRuleFoundException("no " + type.toString() + " edge between " + nodeA + " and " + nodeB); + if (edgeRules.isEmpty()) { + + throw new NoEdgeRuleFoundException("no " + errorMsg); + + } else if (edgeRules.size() == 1) { + + edgeRule = edgeRules.values().iterator().next(); + + } else { + + Optional optionalEdgeRule = Optional.empty(); + + try { + optionalEdgeRule = this.getDefaultEdgeRule(edgeRules); + } catch (MultipleEdgeRuleFoundException e) { + throw new MultipleEdgeRuleFoundException("multiple default edge rule exists " + errorMsg); + } + + edgeRule = optionalEdgeRule.orElseThrow(() -> new MultipleEdgeRuleFoundException("multiple edge rule exists with no default " + errorMsg)); + + } + + return edgeRule; } - + + private Optional getDefaultEdgeRule(Map edgeRules) throws MultipleEdgeRuleFoundException { + + EdgeRule edgeRule = null; + int numDefaults = 0; + + for (Map.Entry entry : edgeRules.entrySet()) { + if (entry.getValue().isDefault()) { + edgeRule = entry.getValue(); + numDefaults++; + } + } + + if (numDefaults > 1) { + throw new MultipleEdgeRuleFoundException(""); + } + + if (edgeRule == null) { + return Optional.empty(); + } else { + return Optional.of(edgeRule); + } + } + + /** + * Gets the rules from the edge rules Json + * + * @param type - type + * @param nodeA - start node + * @param nodeB - end node + * @param label - edge label to filter on + * @return + */ + private List> getEdgeRulesFromJson(EdgeType type, String nodeA, String nodeB, String label) { + if (label == null) { + return rulesDoc.read("$.rules.[?]", buildFilter(type, nodeA, nodeB)); + } else { + return rulesDoc.read("$.rules.[?]", buildFilter(type, nodeA, nodeB, label)); + } + } + /** * Builds a JsonPath filter to search for an edge from nodeA to nodeB with the given edge type (cousin or parent/child) - * + * * @param type * @param nodeA - start node * @param nodeB - end node * @return */ private Filter buildFilter(EdgeType type, String nodeA, String nodeB) { + return this.buildFilter(type, nodeA, nodeB, null); + } + + private Filter buildFilter(EdgeType type, String nodeA, String nodeB, String label) { if (EdgeType.COUSIN.equals(type)) { - return filter( - where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString()) + Filter f = filter( + where("from").is(nodeA) + .and("to").is(nodeB) + .and(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString()) ); + if (label != null) { + f = f.and(where(LABEL).is(label)); + } + + return f; } else { return filter( - where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is("${direction}")).or( - where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is("!${direction}") + where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is(DIRECTION_NOTATION)).or( + where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is(NOT_DIRECTION_NOTATION) ); } } - + /** - * Puts the give edge rule information into an EdgeRule object. - * - * @param edge - the edge information returned from JsonPath + * Puts the give edge rule information into an EdgeRule object. + * + * @param map edge rule property map * @return EdgeRule containing that information */ private EdgeRule buildRule(Map map) { Map edge = new EdgePropertyMap<>(); edge.putAll(map); - + EdgeRule rule = new EdgeRule(); - rule.setLabel(edge.get("label")); + rule.setLabel(edge.get(LABEL)); rule.setDirection(edge.get("direction")); rule.setMultiplicityRule(edge.get("multiplicity")); rule.setContains(edge.get(EdgeProperty.CONTAINS.toString())); rule.setDeleteOtherV(edge.get(EdgeProperty.DELETE_OTHER_V.toString())); rule.setServiceInfrastructure(edge.get(EdgeProperty.SVC_INFRA.toString())); rule.setPreventDelete(edge.get(EdgeProperty.PREVENT_DELETE.toString())); - + if (edge.containsKey("default")) { + rule.setIsDefault(edge.get("default")); + } + return rule; } @@ -403,7 +663,7 @@ public class EdgeRules { * If getEdgeRule gets a request for A|B, and it finds something as B|A, the caller still expects * the returned EdgeRule to reflect A|B directionality. This helper method flips B|A direction to * match this expectation. - * + * * @param rule whose direction needs flipped * @return the updated rule */ @@ -415,7 +675,7 @@ public class EdgeRules { rule.setDirection(Direction.IN); return rule; } else { //direction is BOTH, flipping both is still both - return rule; + return rule; } } @@ -431,14 +691,30 @@ public class EdgeRules { * @throws AAIException if no such edge exists */ public EdgeRule getEdgeRule(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException { + return this.getEdgeRule(type, aVertex, bVertex, null); + } + + /** + * Gets the edge rule of the given type that exists between A and B with label. + * Will check B|A as well, and flips the direction accordingly if that succeeds + * to match the expected A|B return. + * + * @param type - the type of edge you're looking for + * @param aVertex - first node type + * @param bVertex - second node type + * @param label - edge label + * @return EdgeRule describing the rule in terms of A|B, if there is any such rule + * @throws AAIException if no such edge exists + */ + public EdgeRule getEdgeRule(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException { String outType = aVertex.property(AAIProperties.NODE_TYPE).orElse(null); String inType = bVertex.property(AAIProperties.NODE_TYPE).orElse(null); - - return this.getEdgeRule(type, outType, inType); - + return this.getEdgeRule(type, outType, inType, label); + + } - + /** * Validate multiplicity. * @@ -450,44 +726,46 @@ public class EdgeRules { */ private Optional validateMultiplicity(EdgeRule rule, GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) { + Vertex a = aVertex; + Vertex b = bVertex; + if (rule.getDirection().equals(Direction.OUT)) { - + a = aVertex; + b = bVertex; } else if (rule.getDirection().equals(Direction.IN)) { - Vertex tempV = bVertex; - bVertex = aVertex; - aVertex = tempV; + a = bVertex; + b = aVertex; } - - String aVertexType = aVertex.property(AAIProperties.NODE_TYPE).orElse(null); - String bVertexType = bVertex.property(AAIProperties.NODE_TYPE).orElse(null); + + String aVertexType = a.property(AAIProperties.NODE_TYPE).orElse(null); + String bVertexType = b.property(AAIProperties.NODE_TYPE).orElse(null); String label = rule.getLabel(); MultiplicityRule multiplicityRule = rule.getMultiplicityRule(); - List outEdges = traversalSource.V(aVertex).outE(label).where(__.inV().has(AAIProperties.NODE_TYPE, bVertexType)).toList(); - List inEdges = traversalSource.V(bVertex).inE(label).where(__.outV().has(AAIProperties.NODE_TYPE, aVertexType)).toList(); + List outEdges = traversalSource.V(a).outE(label).where(__.inV().has(AAIProperties.NODE_TYPE, bVertexType)).toList(); + List inEdges = traversalSource.V(b).inE(label).where(__.outV().has(AAIProperties.NODE_TYPE, aVertexType)).toList(); String detail = ""; + final String msg = "multiplicity rule violated: only one edge can exist with label: "; if (multiplicityRule.equals(MultiplicityRule.ONE2ONE)) { - if (inEdges.size() >= 1 || outEdges.size() >= 1 ) { - detail = "multiplicity rule violated: only one edge can exist with label: " + label + " between " + aVertexType + " and " + bVertexType; + if (!inEdges.isEmpty() || !outEdges.isEmpty() ) { + detail = msg + label + " between " + aVertexType + " and " + bVertexType; } } else if (multiplicityRule.equals(MultiplicityRule.ONE2MANY)) { - if (inEdges.size() >= 1) { - detail = "multiplicity rule violated: only one edge can exist with label: " + label + " between " + aVertexType + " and " + bVertexType; + if (!inEdges.isEmpty()) { + detail = msg + label + " between " + aVertexType + " and " + bVertexType; } } else if (multiplicityRule.equals(MultiplicityRule.MANY2ONE)) { - if (outEdges.size() >= 1) { - detail = "multiplicity rule violated: only one edge can exist with label: " + label + " between " + aVertexType + " and " + bVertexType; + if (!outEdges.isEmpty()) { + detail = msg + label + " between " + aVertexType + " and " + bVertexType; } - } else { - } - + if (!"".equals(detail)) { return Optional.of(detail); } else { return Optional.empty(); } - - + + } /** @@ -513,7 +791,7 @@ public class EdgeRules { } } } - + /** * Reads all the edge rules from the loaded json file. * @@ -547,22 +825,22 @@ public class EdgeRules { /** * Gets all the edge rules we define. - * + * * @return Multimap */ public Multimap getAllRules() { Multimap result = ArrayListMultimap.create(); - + List> rules = readRules(); for (Map rule : rules) { EdgeRule er = buildRule(rule); String name = rule.get("from") + "|" + rule.get("to"); result.put(name, er); } - + return result; } - + /** * Gets all edge rules that define a child relationship from * the given node type. @@ -571,19 +849,40 @@ public class EdgeRules { * @return */ public Set getChildren(String nodeType) { - + final Filter filter = filter( - where("from").is(nodeType).and(EdgeProperty.CONTAINS.toString()).is("${direction}") - ).or(where("to").is(nodeType).and(EdgeProperty.CONTAINS.toString()).is("!${direction}")); - + where("from").is(nodeType).and(EdgeProperty.CONTAINS.toString()).is(DIRECTION_NOTATION) + ).or(where("to").is(nodeType).and(EdgeProperty.CONTAINS.toString()).is(NOT_DIRECTION_NOTATION)); + final List> rules = readRules(filter); final Set result = new HashSet<>(); rules.forEach(item -> { verifyRule(item); result.add(buildRule(item)); }); - + return result; - + + } + + private static class Helper { + private static final EdgeRules INSTANCE = new EdgeRules(); + private static final Map INSTANCEMAP = new ConcurrentHashMap<>(); + + private Helper() {} + + private static EdgeRules getEdgeRulesByFilename(String rulesFilename) { + return new EdgeRules(rulesFilename); + } + + private static EdgeRules getVersionedEdgeRules(Version v) { + if (Version.isLatest(v)) { + return INSTANCE; + } + if (!INSTANCEMAP.containsKey(v)) { + INSTANCEMAP.put(v, new EdgeRules(v)); + } + return INSTANCEMAP.get(v); + } } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/MultipleEdgeRuleFoundException.java b/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/MultipleEdgeRuleFoundException.java new file mode 100644 index 00000000..bb4ba4df --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/MultipleEdgeRuleFoundException.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aai.serialization.db.exceptions; + +import org.onap.aai.exceptions.AAIException; + +public class MultipleEdgeRuleFoundException extends AAIException { + + private static final long serialVersionUID = -906843868234976763L; + + public MultipleEdgeRuleFoundException(String message) { + super("AAI_6107", message); + } + + public MultipleEdgeRuleFoundException(Throwable cause) { + super("AAI_6107",cause); + } + + public MultipleEdgeRuleFoundException(String message, Throwable cause) { + super("AAI_6107", cause, message); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/NoEdgeRuleFoundException.java b/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/NoEdgeRuleFoundException.java index 247379cb..b66f0147 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/NoEdgeRuleFoundException.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/exceptions/NoEdgeRuleFoundException.java @@ -28,14 +28,14 @@ public class NoEdgeRuleFoundException extends AAIException { private static final long serialVersionUID = -906843868234976763L; public NoEdgeRuleFoundException(String message) { - super("AAI_6129", message); + super("AAI_6107", message); } public NoEdgeRuleFoundException(Throwable cause) { - super("AAI_6129",cause); + super("AAI_6107",cause); } public NoEdgeRuleFoundException(String message, Throwable cause) { - super("AAI_6129", cause, message); + super("AAI_6107", cause, message); } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/util/VersionChecker.java b/aai-core/src/main/java/org/onap/aai/serialization/db/util/VersionChecker.java new file mode 100644 index 00000000..04c57ed8 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/util/VersionChecker.java @@ -0,0 +1,39 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.serialization.db.util; + +import org.onap.aai.introspection.Version; + +public class VersionChecker { + + private VersionChecker() {} + + /** + * true if Version v is gt v12 + * + * @param v - Version to be checked + * @return + */ + public static boolean apiVersionNeedsEdgeLabel(Version v) { + return v.compareTo(Version.v12) >= 0 ; + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/RawFormat.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/RawFormat.java index 7aaf3035..3477a748 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/RawFormat.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/RawFormat.java @@ -25,6 +25,7 @@ import java.util.Iterator; import java.util.List; import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.onap.aai.db.props.AAIProperties; @@ -101,23 +102,35 @@ public class RawFormat implements FormatMapper { return json; } + protected JsonArray createRelationshipObject(Vertex v) throws AAIFormatVertexException { JsonArray jarray = new JsonArray(); - Iterator iter = v.vertices(Direction.BOTH); - - while (iter.hasNext()) { - Vertex related = iter.next(); + Iterator inIter = v.edges(Direction.IN); + Iterator outIter = v.edges(Direction.OUT); - JsonObject json = new JsonObject(); - json.addProperty("id", related.id().toString()); - json.addProperty("node-type", related.value(AAIProperties.NODE_TYPE)); - json.addProperty("url", this.urlBuilder.pathed(related)); - jarray.add(json); + while (inIter.hasNext()) { + Edge e = inIter.next(); + jarray.add(this.getRelatedObject(e.label(), e.outVertex())); + } + + while (outIter.hasNext()) { + Edge e = outIter.next(); + jarray.add(this.getRelatedObject(e.label(), e.inVertex())); } return jarray; } + private JsonObject getRelatedObject(String label, Vertex related) throws AAIFormatVertexException { + JsonObject json = new JsonObject(); + json.addProperty("id", related.id().toString()); + json.addProperty("relationship-label", label); + json.addProperty("node-type", related.value(AAIProperties.NODE_TYPE)); + json.addProperty("url", this.urlBuilder.pathed(related)); + + return json; + } + public static class Builder implements NodesOnly, Depth { protected final Loader loader; diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/exceptions/AAIFormatQueryResultFormatNotSupported.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/exceptions/AAIFormatQueryResultFormatNotSupported.java new file mode 100644 index 00000000..726116a0 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/exceptions/AAIFormatQueryResultFormatNotSupported.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aai.serialization.queryformats.exceptions; + +public class AAIFormatQueryResultFormatNotSupported extends Exception { + + private static final long serialVersionUID = -5814240842844624097L; + + public AAIFormatQueryResultFormatNotSupported() {} + + public AAIFormatQueryResultFormatNotSupported(String message) { + super(message); + } + + public AAIFormatQueryResultFormatNotSupported(Throwable cause) { + super(cause); + } + + public AAIFormatQueryResultFormatNotSupported(String message, Throwable cause) { + super(message, cause); + } +} -- cgit 1.2.3-korg