Update the license for 2017-2018 license
[aai/traversal.git] / aai-traversal / src / main / java / org / onap / aai / rest / search / GenericQueryProcessor.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *    http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 package org.onap.aai.rest.search;
21
22 import java.io.FileNotFoundException;
23 import java.net.URI;
24 import java.util.*;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27
28 import javax.ws.rs.core.MultivaluedHashMap;
29 import javax.ws.rs.core.MultivaluedMap;
30
31 import org.apache.tinkerpop.gremlin.process.traversal.P;
32 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
33 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
34 import org.apache.tinkerpop.gremlin.structure.Graph;
35 import org.apache.tinkerpop.gremlin.structure.Vertex;
36 import org.javatuples.Pair;
37 import org.onap.aai.query.builder.MissingOptionalParameter;
38 import org.onap.aai.rest.dsl.DslQueryProcessor;
39 import org.onap.aai.restcore.util.URITools;
40 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
41 import org.onap.aai.serialization.queryformats.SubGraphStyle;
42
43 import jersey.repackaged.com.google.common.base.Joiner;
44
45 public abstract class GenericQueryProcessor {
46
47         protected final Optional<URI> uri;
48         protected final MultivaluedMap<String, String> queryParams;
49         protected final Optional<Collection<Vertex>> vertices;
50         protected static Pattern p = Pattern.compile("query/(.*+)");
51         protected Optional<String> gremlin;
52         protected final TransactionalGraphEngine dbEngine;
53         protected static GremlinServerSingleton gremlinServerSingleton = GremlinServerSingleton.getInstance();
54         protected static GroovyQueryBuilderSingleton queryBuilderSingleton = GroovyQueryBuilderSingleton.getInstance();
55         protected final boolean isGremlin;
56         /* dsl parameters to store dsl query and to check 
57          * if this is a DSL request
58          */
59         protected Optional<String> dsl;
60         protected final boolean isDsl ;
61         
62         protected GenericQueryProcessor(Builder builder) {
63                 this.uri = builder.getUri();
64                 this.dbEngine = builder.getDbEngine();
65                 this.vertices = builder.getVertices();
66                 this.gremlin = builder.getGremlin();
67                 this.isGremlin = builder.isGremlin();
68                 this.dsl = builder.getDsl();
69                 this.isDsl = builder.isDsl();
70                 
71                 if (uri.isPresent()) {
72                         queryParams = URITools.getQueryMap(uri.get());
73                 } else {
74                         queryParams = new MultivaluedHashMap<>();
75                 }
76         }
77         
78         protected abstract GraphTraversal<?,?> runQuery(String query, Map<String, Object> params);
79         
80         protected List<Object> processSubGraph(SubGraphStyle style, GraphTraversal<?,?> g) {
81                 final List<Object> resultVertices = new Vector<>();
82                 g.store("x");
83                 
84                 if (SubGraphStyle.prune.equals(style) || SubGraphStyle.star.equals(style)) {
85                         g.barrier().bothE();
86                         if (SubGraphStyle.prune.equals(style)) {
87                                 g.where(__.otherV().where(P.within("x")));
88                         }
89                         g.dedup().subgraph("subGraph").cap("subGraph").map(x -> (Graph)x.get()).next().traversal().V().forEachRemaining(x -> {
90                                 resultVertices.add(x);
91                         });
92                 } else {
93                         resultVertices.addAll(g.toList());
94                 }
95                 return resultVertices;
96         }
97         
98         public List<Object> execute(SubGraphStyle style) throws FileNotFoundException {
99                 final List<Object> resultVertices;
100
101                 Pair<String, Map<String, Object>> tuple = this.createQuery();
102                 String query = tuple.getValue0();
103                 Map<String, Object> params = tuple.getValue1();
104
105                 if (query.equals("") && (vertices.isPresent() && vertices.get().isEmpty())) {
106                         //nothing to do, just exit
107                         return new ArrayList<>();
108                 }
109                 GraphTraversal<?,?> g = this.runQuery(query, params);
110                 
111                 resultVertices = this.processSubGraph(style, g);
112                 
113                 return resultVertices;
114         }
115         
116         protected Pair<String, Map<String, Object>> createQuery() {
117                 Map<String, Object> params = new HashMap<>();
118                 String query = "";
119                  if (this.isGremlin) {
120                         query = gremlin.get();
121                         
122                 }else if (this.isDsl) {
123                         String dslUserQuery = dsl.get();
124                         String dslQuery = new DslQueryProcessor.Builder().build(dslUserQuery);
125                         
126                         query = queryBuilderSingleton.executeTraversal(dbEngine, dslQuery, params);
127                         String startPrefix = "g.V()";
128                         query = startPrefix + query;
129                         
130                 }else {
131                         Matcher m = p.matcher(uri.get().getPath());
132                         String queryName = "";
133                         List<String> optionalParameters = Collections.emptyList();
134                         if (m.find()) {
135                                 queryName = m.group(1);
136                                 CustomQueryConfig queryConfig = gremlinServerSingleton.getCustomQueryConfig(queryName);
137                                 if ( queryConfig != null ) {
138                                         query = queryConfig.getQuery();
139                                         optionalParameters = queryConfig.getQueryOptionalProperties();
140                                 }
141                         }
142                         
143                         for (String key : queryParams.keySet()) {
144                                 params.put(key, queryParams.getFirst(key));
145                                 if ( optionalParameters.contains(key) ){
146                                         optionalParameters.remove(key);
147                                 }
148                         }
149                         
150                         if (!optionalParameters.isEmpty()){
151                                 MissingOptionalParameter missingParameter = MissingOptionalParameter.getInstance();
152                                 for ( String key : optionalParameters ) {
153                                         params.put(key, missingParameter);
154                                 }
155                         }
156                         
157                         if (vertices.isPresent() && !vertices.get().isEmpty()) {
158
159                                 // Get the vertices and convert them into object array
160                                 // The reason for this was .V() takes in an array of objects
161                                 // not a list of objects so that needs to be converted
162                 // Also instead of statically creating the list which is a bad practice
163                                 // We are binding the array dynamically to the groovy processor correctly
164                                 // This will fix the memory issue of the method size too big
165                                 // as statically creating a list string and passing is not appropriate
166                                 params.put("startVertexes", vertices.get().toArray());
167
168                                 if (query == null) {
169                                         query = "";
170                                 } else {
171                                         query = queryBuilderSingleton.executeTraversal(dbEngine, query, params);
172                                 }
173
174                                 String startPrefix = "g.V(startVertexes)";
175
176                                 if (!"".equals(query)) {
177                                         query = startPrefix + query;
178                                 } else {
179                                         query = startPrefix;
180                                 }
181                         }
182                         
183                 }
184                 
185                 return new Pair<>(query, params);
186         }
187         
188         public static class Builder {
189
190                 private final TransactionalGraphEngine dbEngine;
191                 private Optional<URI> uri = Optional.empty();
192                 private Optional<String> gremlin = Optional.empty();
193                 private boolean isGremlin = false;
194                 private Optional<Collection<Vertex>> vertices = Optional.empty();
195                 private QueryProcessorType processorType = QueryProcessorType.GREMLIN_SERVER;
196                 
197                 private Optional<String> dsl = Optional.empty();
198                 private boolean isDsl = false;
199                 
200                 public Builder(TransactionalGraphEngine dbEngine) {
201                         this.dbEngine = dbEngine;
202                 }
203                 
204                 public Builder queryFrom(URI uri) {
205                         this.uri = Optional.of(uri);
206                         this.isGremlin = false;
207                         return this;
208                 }
209                 
210                 public Builder startFrom(Collection<Vertex> vertices) {
211                         this.vertices = Optional.of(vertices);
212                         return this;
213                 }
214                 
215                 public Builder queryFrom( String query, String queryType) {
216                         
217                         if(queryType.equals("gremlin")){
218                                 this.gremlin = Optional.of(query);
219                                 this.isGremlin = true;
220                         }
221                         if(queryType.equals("dsl")){
222                                 this.dsl = Optional.of(query);
223                                 this.isDsl = true;
224                         }
225                         return this;
226                 }
227                 
228                 public Builder processWith(QueryProcessorType type) {
229                         this.processorType = type;
230                         return this;
231                 }
232                 public TransactionalGraphEngine getDbEngine() {
233                         return dbEngine;
234                 }
235
236                 public Optional<URI> getUri() {
237                         return uri;
238                 }
239
240                 public Optional<String> getGremlin() {
241                         return gremlin;
242                 }
243
244                 public boolean isGremlin() {
245                         return isGremlin;
246                 }
247                 
248                 public Optional<String> getDsl() {
249                         return dsl;
250                 }
251
252                 public boolean isDsl() {
253                         return isDsl;
254                 }
255
256                 public Optional<Collection<Vertex>> getVertices() {
257                         return vertices;
258                 }
259                 
260                 public QueryProcessorType getProcessorType() {
261                         return processorType;
262                 }
263                 
264                 public GenericQueryProcessor create() {
265                         
266                         if (this.getProcessorType().equals(QueryProcessorType.GREMLIN_SERVER)) {
267                                 return new GremlinServerImpl(this);
268                         } else if (this.getProcessorType().equals(QueryProcessorType.LOCAL_GROOVY)) {
269                                 return new GroovyShellImpl(this);
270                         } else {
271                                 return new GremlinServerImpl(this);
272                         }
273                 }
274                 
275         }
276 }