/** * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ * Copyright © 2017-18 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Modifications Copyright © 2018 IBM. * ================================================================================ * 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.edges; import org.apache.commons.lang.StringUtils; import org.onap.aai.edges.enums.AAIDirection; import org.onap.aai.edges.enums.EdgeField; import org.onap.aai.edges.enums.EdgeProperty; import org.onap.aai.edges.enums.EdgeType; import org.onap.aai.setup.SchemaVersion; import com.jayway.jsonpath.Filter; import com.jayway.jsonpath.Predicate; import static com.jayway.jsonpath.Filter.filter; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; import static com.jayway.jsonpath.Criteria.where; /** * For querying the edge rules schema (not the database) * */ public class EdgeRuleQuery { private Filter filter; private Optional v; private String nodeA; private String nodeB; private String label; private AAIDirection direction; private EdgeType type; private boolean isPrivate; public static class Builder { private static final String TO_ONLY = "ToOnly"; private static final String FROM_ONLY = "FromOnly"; //required private String nodeA; //optional - null will translate to any value of the param private String nodeB = null; private String label = null; private EdgeType type = null; private AAIDirection direction = null; private boolean isPrivate = false; private SchemaVersion version = null; public Builder(String nodeA) { this.nodeA = nodeA; } public Builder(String nodeA, String nodeB) { this.nodeA = nodeA; this.nodeB = nodeB; } private String getFirstNodeType() { return nodeA; } public Builder fromOnly() { this.nodeB = FROM_ONLY; return this; } private String getSecondNodeType() { return nodeB; } public Builder to(String nodeB){ this.nodeB = nodeB; return this; } public Builder toOnly() { //Allows this to be used with single parameter constructor Builder(String nodeA) if(StringUtils.isEmpty(this.nodeB) && StringUtils.isNotEmpty(this.nodeA) ) { this.nodeB=this.nodeA; } this.nodeA = TO_ONLY; return this; } public Builder label(String label) { this.label = label; return this; } private String getLabel() { return label; } public Builder edgeType(EdgeType type) { this.type = type; return this; } private EdgeType getEdgeType() { return type; } public Builder direction(AAIDirection direction) { this.direction = direction; return this; } private AAIDirection getDirection() { return direction; } public Builder version(SchemaVersion version) { this.version = version; return this; } public Builder setPrivate(boolean isPrivate){ this.isPrivate = isPrivate; return this; } public boolean isPrivate(){ return isPrivate; } private Optional getSchemaVersion() { return Optional.ofNullable(version); } public EdgeRuleQuery build() { return new EdgeRuleQuery(this); } } private EdgeRuleQuery(Builder builder) { this.v = builder.getSchemaVersion(); this.nodeA = builder.getFirstNodeType(); this.nodeB = builder.getSecondNodeType(); this.label = builder.getLabel(); this.type = builder.getEdgeType(); this.direction = builder.getDirection(); this.isPrivate = builder.isPrivate(); //will cover from A to B case List criteriaFromTo = new ArrayList<>(); //Special logic to allow for A to B case only if(("FromOnly").equals(builder.getSecondNodeType())) { criteriaFromTo.add(buildToFromPart(builder.getFirstNodeType(), null)); } else { criteriaFromTo.add(buildToFromPart(builder.getFirstNodeType(), builder.getSecondNodeType())); } //will cover from B to A case - must be separate bc jsonpath won't let me OR predicates >:C List criteriaToFrom = new ArrayList<>(); //Special logic to allow for B to A case only if(("ToOnly").equals(builder.getFirstNodeType())) { criteriaToFrom.add(buildToFromPart(null, builder.getSecondNodeType())); } else { criteriaToFrom.add(buildToFromPart(builder.getSecondNodeType(), builder.getFirstNodeType())); } if (builder.getLabel() != null) { Predicate labelPred = addLabel(builder.getLabel()); criteriaFromTo.add(labelPred); criteriaToFrom.add(labelPred); } if (builder.getEdgeType() != null) { Predicate typePred = addType(builder.getEdgeType()); criteriaFromTo.add(typePred); criteriaToFrom.add(typePred); } Predicate privatePredicate = where("private").is(String.valueOf(isPrivate)); if(isPrivate){ criteriaFromTo.add(privatePredicate); criteriaToFrom.add(privatePredicate); } if (builder.getDirection() != null) { Predicate directionPred = addDirection(builder.getDirection()); criteriaFromTo.add(directionPred); criteriaToFrom.add(directionPred); } if(("ToOnly").equals(builder.getFirstNodeType())) { this.filter = filter(criteriaToFrom); } else if(("FromOnly").equals(builder.getSecondNodeType())) { this.filter = filter(criteriaFromTo); } else { this.filter = filter(criteriaFromTo).or(filter(criteriaToFrom)); } } private Predicate buildToFromPart(String from, String to) { if (from == null && to == null) { //shouldn't ever happen though throw new IllegalStateException("must have at least one node defined"); } Predicate p; if (to == null) { p = where(EdgeField.FROM.toString()).is(from); } else if (from == null) { p = where(EdgeField.TO.toString()).is(to); } else { p = where(EdgeField.FROM.toString()).is(from).and(EdgeField.TO.toString()).is(to); } return p; } private Predicate addLabel(String label) { return where(EdgeField.LABEL.toString()).is(label); } private Predicate addType(EdgeType type) { if (type == EdgeType.COUSIN) { return where(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString()); } else { //equals TREE return where(EdgeProperty.CONTAINS.toString()).ne(AAIDirection.NONE.toString()); } } private Predicate addDirection(AAIDirection direction) { if (direction == AAIDirection.OUT) { return where(EdgeField.DIRECTION.toString()).in(AAIDirection.OUT.toString(), AAIDirection.BOTH.toString()); } else if (direction == AAIDirection.IN) { return where(EdgeField.DIRECTION.toString()).in(AAIDirection.IN.toString(), AAIDirection.BOTH.toString()); } else if (direction == AAIDirection.BOTH) { return where(EdgeField.DIRECTION.toString()).is(AAIDirection.BOTH.toString()); } else if (direction == AAIDirection.NONE) { return where(EdgeField.DIRECTION.toString()).is(AAIDirection.NONE.toString()); } return where(EdgeField.DIRECTION.toString()).is(AAIDirection.NONE.toString()); } /** * Provides the JsonPath filter for actually querying the edge rule schema files * @return Filter */ public Filter getFilter() { return this.filter; } /** * Gets the first node type given for the query. * * ie, If you called Builder(A,B) this would return A, * if you called Builder(B,A), it would return B, * if you called Builder(A), it would return A. * * This is to maintain backwards compatibility with the * EdgeRules API which flipped the direction of * the result EdgeRule to match the input directionality. * * @return String first node type of the query */ public String getFromType() { return this.nodeA; } /** * So the Ingestor knows which version of the rules to search * @return the Version */ public Optional getVersion() { return this.v; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("EdgeRuleQuery with filter params node type: ").append(nodeA); if (nodeB != null) { sb.append(", node type: ").append(nodeB); } if (label != null) { sb.append(", label: ").append(label); } sb.append(", type: "); if (type != null) { sb.append(type.toString()); } else { sb.append("any"); } sb.append(", isPrivate: "); sb.append(isPrivate); if(v.isPresent()){ sb.append(", for version: ").append(v.get().toString()).append("."); } return sb.toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EdgeRuleQuery ruleQuery = (EdgeRuleQuery) o; return isPrivate == ruleQuery.isPrivate && Objects.equals(v, ruleQuery.v) && Objects.equals(nodeA, ruleQuery.nodeA) && Objects.equals(nodeB, ruleQuery.nodeB) && Objects.equals(label, ruleQuery.label) && direction == ruleQuery.direction && type == ruleQuery.type; } @Override public int hashCode() { if(v.isPresent()){ return Objects.hash(v.get(), nodeA, nodeB, label, direction, type, isPrivate); } else { return Objects.hash(nodeA, nodeB, label, direction, type, isPrivate); } } }