fc58dbb61189de146ffcb9096be9e6e044b5d406
[aai/aai-common.git] / aai-schema-ingest / src / main / java / org / onap / aai / edges / EdgeRuleQuery.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-18 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  *  Modifications Copyright © 2018 IBM.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *   http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.aai.edges;
24
25 import static com.jayway.jsonpath.Criteria.where;
26 import static com.jayway.jsonpath.Filter.filter;
27
28 import com.jayway.jsonpath.Filter;
29 import com.jayway.jsonpath.Predicate;
30
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Objects;
34 import java.util.Optional;
35
36 import org.apache.commons.lang.StringUtils;
37 import org.onap.aai.edges.enums.AAIDirection;
38 import org.onap.aai.edges.enums.EdgeField;
39 import org.onap.aai.edges.enums.EdgeProperty;
40 import org.onap.aai.edges.enums.EdgeType;
41 import org.onap.aai.setup.SchemaVersion;
42
43 /**
44  * For querying the edge rules schema (not the database)
45  *
46  */
47 public class EdgeRuleQuery {
48     private Filter filter;
49     private Optional<SchemaVersion> v;
50     private String nodeA;
51     private String nodeB;
52     private String label;
53     private AAIDirection direction;
54     private EdgeType type;
55     private boolean isPrivate;
56
57     public static class Builder {
58         private static final String TO_ONLY = "ToOnly";
59
60         private static final String FROM_ONLY = "FromOnly";
61
62         // required
63         private String nodeA;
64
65         // optional - null will translate to any value of the param
66         private String nodeB = null;
67         private String label = null;
68         private EdgeType type = null;
69         private AAIDirection direction = null;
70         private boolean isPrivate = false;
71         private SchemaVersion version = null;
72
73         public Builder(String nodeA) {
74             this.nodeA = nodeA;
75         }
76
77         public Builder(String nodeA, String nodeB) {
78             this.nodeA = nodeA;
79             this.nodeB = nodeB;
80         }
81
82         private String getFirstNodeType() {
83             return nodeA;
84         }
85
86         public Builder fromOnly() {
87             this.nodeB = FROM_ONLY;
88             return this;
89         }
90
91         private String getSecondNodeType() {
92             return nodeB;
93         }
94
95         public Builder to(String nodeB) {
96             this.nodeB = nodeB;
97             return this;
98         }
99
100         public Builder toOnly() {
101             // Allows this to be used with single parameter constructor Builder(String nodeA)
102             if (StringUtils.isEmpty(this.nodeB) && StringUtils.isNotEmpty(this.nodeA)) {
103                 this.nodeB = this.nodeA;
104             }
105             this.nodeA = TO_ONLY;
106             return this;
107         }
108
109         public Builder label(String label) {
110             this.label = label;
111             return this;
112         }
113
114         private String getLabel() {
115             return label;
116         }
117
118         public Builder edgeType(EdgeType type) {
119             this.type = type;
120             return this;
121         }
122
123         private EdgeType getEdgeType() {
124             return type;
125         }
126
127         public Builder direction(AAIDirection direction) {
128             this.direction = direction;
129             return this;
130         }
131
132         private AAIDirection getDirection() {
133             return direction;
134         }
135
136         public Builder version(SchemaVersion version) {
137             this.version = version;
138             return this;
139         }
140
141         public Builder setPrivate(boolean isPrivate) {
142             this.isPrivate = isPrivate;
143             return this;
144         }
145
146         public boolean isPrivate() {
147             return isPrivate;
148         }
149
150         private Optional<SchemaVersion> getSchemaVersion() {
151             return Optional.ofNullable(version);
152         }
153
154         public EdgeRuleQuery build() {
155             return new EdgeRuleQuery(this);
156         }
157     }
158
159     private EdgeRuleQuery(Builder builder) {
160         this.v = builder.getSchemaVersion();
161         this.nodeA = builder.getFirstNodeType();
162         this.nodeB = builder.getSecondNodeType();
163         this.label = builder.getLabel();
164         this.type = builder.getEdgeType();
165         this.direction = builder.getDirection();
166         this.isPrivate = builder.isPrivate();
167
168         // will cover from A to B case
169         List<Predicate> criteriaFromTo = new ArrayList<>();
170         // Special logic to allow for A to B case only
171         if (("FromOnly").equals(builder.getSecondNodeType())) {
172             criteriaFromTo.add(buildToFromPart(builder.getFirstNodeType(), null));
173         } else {
174             criteriaFromTo.add(buildToFromPart(builder.getFirstNodeType(), builder.getSecondNodeType()));
175         }
176         // will cover from B to A case - must be separate bc jsonpath won't let me OR predicates >:C
177         List<Predicate> criteriaToFrom = new ArrayList<>();
178         // Special logic to allow for B to A case only
179         if (("ToOnly").equals(builder.getFirstNodeType())) {
180             criteriaToFrom.add(buildToFromPart(null, builder.getSecondNodeType()));
181         } else {
182             criteriaToFrom.add(buildToFromPart(builder.getSecondNodeType(), builder.getFirstNodeType()));
183         }
184         if (builder.getLabel() != null) {
185             Predicate labelPred = addLabel(builder.getLabel());
186             criteriaFromTo.add(labelPred);
187             criteriaToFrom.add(labelPred);
188         }
189
190         if (builder.getEdgeType() != null && builder.getEdgeType() != EdgeType.ALL) {
191             Predicate typePred = addType(builder.getEdgeType());
192             criteriaFromTo.add(typePred);
193             criteriaToFrom.add(typePred);
194         }
195         Predicate privatePredicate = where("private").is(String.valueOf(isPrivate));
196
197         if (isPrivate) {
198             criteriaFromTo.add(privatePredicate);
199             criteriaToFrom.add(privatePredicate);
200         }
201
202         if (builder.getDirection() != null) {
203             Predicate directionPred = addDirection(builder.getDirection());
204             criteriaFromTo.add(directionPred);
205             criteriaToFrom.add(directionPred);
206         }
207         if (("ToOnly").equals(builder.getFirstNodeType())) {
208             this.filter = filter(criteriaToFrom);
209         } else if (("FromOnly").equals(builder.getSecondNodeType())) {
210             this.filter = filter(criteriaFromTo);
211         } else {
212             this.filter = filter(criteriaFromTo).or(filter(criteriaToFrom));
213         }
214     }
215
216     private Predicate buildToFromPart(String from, String to) {
217         if (from == null && to == null) { // shouldn't ever happen though
218             throw new IllegalStateException("must have at least one node defined");
219         }
220
221         Predicate p;
222
223         if (to == null) {
224             p = where(EdgeField.FROM.toString()).is(from);
225         } else if (from == null) {
226             p = where(EdgeField.TO.toString()).is(to);
227         } else {
228             p = where(EdgeField.FROM.toString()).is(from).and(EdgeField.TO.toString()).is(to);
229         }
230
231         return p;
232     }
233
234     private Predicate addLabel(String label) {
235         return where(EdgeField.LABEL.toString()).is(label);
236     }
237
238     private Predicate addType(EdgeType type) {
239         if (type == EdgeType.COUSIN) {
240             return where(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString());
241         } else { // equals TREE
242             return where(EdgeProperty.CONTAINS.toString()).ne(AAIDirection.NONE.toString());
243         }
244     }
245
246     private Predicate addDirection(AAIDirection direction) {
247         if (direction == AAIDirection.OUT) {
248             return where(EdgeField.DIRECTION.toString()).in(AAIDirection.OUT.toString(), AAIDirection.BOTH.toString());
249         } else if (direction == AAIDirection.IN) {
250             return where(EdgeField.DIRECTION.toString()).in(AAIDirection.IN.toString(), AAIDirection.BOTH.toString());
251         } else if (direction == AAIDirection.BOTH) {
252             return where(EdgeField.DIRECTION.toString()).is(AAIDirection.BOTH.toString());
253         } else if (direction == AAIDirection.NONE) {
254             return where(EdgeField.DIRECTION.toString()).is(AAIDirection.NONE.toString());
255         }
256         return where(EdgeField.DIRECTION.toString()).is(AAIDirection.NONE.toString());
257     }
258
259     /**
260      * Provides the JsonPath filter for actually querying the edge rule schema files
261      * 
262      * @return Filter
263      */
264     public Filter getFilter() {
265         return this.filter;
266     }
267
268     /**
269      * Gets the first node type given for the query.
270      *
271      * ie, If you called Builder(A,B) this would return A,
272      * if you called Builder(B,A), it would return B,
273      * if you called Builder(A), it would return A.
274      *
275      * This is to maintain backwards compatibility with the
276      * EdgeRules API which flipped the direction of
277      * the result EdgeRule to match the input directionality.
278      *
279      * @return String first node type of the query
280      */
281     public String getFromType() {
282         return this.nodeA;
283     }
284
285     /**
286      * So the Ingestor knows which version of the rules to search
287      * 
288      * @return the Version
289      */
290     public Optional<SchemaVersion> getVersion() {
291         return this.v;
292     }
293
294     @Override
295     public String toString() {
296         StringBuilder sb = new StringBuilder();
297
298         sb.append("EdgeRuleQuery with filter params node type: ").append(nodeA);
299
300         if (nodeB != null) {
301             sb.append(", node type: ").append(nodeB);
302         }
303
304         if (label != null) {
305             sb.append(", label: ").append(label);
306         }
307
308         sb.append(", type: ");
309         if (type != null) {
310             sb.append(type.toString());
311         } else {
312             sb.append("any");
313         }
314
315         sb.append(", isPrivate: ");
316         sb.append(isPrivate);
317
318         if (v.isPresent()) {
319             sb.append(", for version: ").append(v.get().toString()).append(".");
320         }
321         return sb.toString();
322     }
323
324     @Override
325     public boolean equals(Object o) {
326         if (this == o)
327             return true;
328         if (o == null || getClass() != o.getClass())
329             return false;
330         EdgeRuleQuery ruleQuery = (EdgeRuleQuery) o;
331         return isPrivate == ruleQuery.isPrivate && Objects.equals(v, ruleQuery.v)
332                 && Objects.equals(nodeA, ruleQuery.nodeA) && Objects.equals(nodeB, ruleQuery.nodeB)
333                 && Objects.equals(label, ruleQuery.label) && direction == ruleQuery.direction && type == ruleQuery.type;
334     }
335
336     @Override
337     public int hashCode() {
338         if (v.isPresent()) {
339             return Objects.hash(v.get(), nodeA, nodeB, label, direction, type, isPrivate);
340         } else {
341             return Objects.hash(nodeA, nodeB, label, direction, type, isPrivate);
342         }
343     }
344
345 }