2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
20 package org.onap.aai.rest;
23 import java.util.ArrayList;
24 import java.util.LinkedHashSet;
25 import java.util.List;
27 import java.util.concurrent.TimeUnit;
28 import java.util.concurrent.Callable;
30 import javax.servlet.http.HttpServletRequest;
31 import javax.ws.rs.Consumes;
32 import javax.ws.rs.DefaultValue;
33 import javax.ws.rs.Encoded;
34 import javax.ws.rs.PUT;
35 import javax.ws.rs.Path;
36 import javax.ws.rs.PathParam;
37 import javax.ws.rs.Produces;
38 import javax.ws.rs.QueryParam;
39 import javax.ws.rs.core.Context;
40 import javax.ws.rs.core.HttpHeaders;
41 import javax.ws.rs.core.MediaType;
42 import javax.ws.rs.core.MultivaluedMap;
43 import javax.ws.rs.core.Response;
44 import javax.ws.rs.core.Response.Status;
45 import javax.ws.rs.core.UriInfo;
47 import org.apache.tinkerpop.gremlin.structure.Vertex;
48 import org.onap.aai.dbmap.DBConnectionType;
49 import org.onap.aai.exceptions.AAIException;
50 import org.onap.aai.introspection.ModelType;
51 import org.onap.aai.introspection.Version;
52 import org.onap.aai.logging.ErrorLogHelper;
53 import org.onap.aai.parsers.query.QueryParser;
54 import org.onap.aai.rest.db.HttpEntry;
55 import org.onap.aai.rest.search.CustomQueryConfig;
56 import org.onap.aai.rest.search.GenericQueryProcessor;
57 import org.onap.aai.rest.search.GremlinServerSingleton;
58 import org.onap.aai.rest.search.QueryProcessorType;
59 import org.onap.aai.restcore.HttpMethod;
60 import org.onap.aai.restcore.RESTAPI;
61 import org.onap.aai.restcore.util.URITools;
62 import org.onap.aai.serialization.db.DBSerializer;
63 import org.onap.aai.serialization.engines.QueryStyle;
64 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
65 import org.onap.aai.serialization.queryformats.Format;
66 import org.onap.aai.serialization.queryformats.FormatFactory;
67 import org.onap.aai.serialization.queryformats.Formatter;
68 import org.onap.aai.serialization.queryformats.SubGraphStyle;
69 import com.att.eelf.configuration.EELFLogger;
70 import com.att.eelf.configuration.EELFManager;
71 import org.onap.aai.logging.LoggingContext;
72 import org.onap.aai.logging.LoggingContext.StatusCode;
73 import org.onap.aai.logging.StopWatch;
75 import com.google.gson.JsonElement;
76 import com.google.gson.JsonObject;
77 import com.google.gson.JsonParser;
78 import org.onap.aai.util.AAIConstants;
80 @Path("{version: v9|v1[0123]}/query")
81 public class QueryConsumer extends RESTAPI {
83 private static final String DEPTH = "depth";
85 /** The introspector factory type. */
86 private ModelType introspectorFactoryType = ModelType.MOXY;
88 private QueryProcessorType processorType = QueryProcessorType.LOCAL_GROOVY;
89 /** The query style. */
90 private QueryStyle queryStyle = QueryStyle.TRAVERSAL;
92 private static final String TARGET_ENTITY = "DB";
93 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(QueryConsumer.class);
96 @Consumes({ MediaType.APPLICATION_JSON})
97 @Produces({ MediaType.APPLICATION_JSON})
98 public Response executeQuery(String content, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @DefaultValue("graphson") @QueryParam("format") String queryFormat,@DefaultValue("no_op") @QueryParam("subgraph") String subgraph, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req){
99 return runner(AAIConstants.AAI_TRAVERSAL_TIMEOUT_ENABLED,
100 AAIConstants.AAI_TRAVERSAL_TIMEOUT_APP,
101 AAIConstants.AAI_TRAVERSAL_TIMEOUT_LIMIT,
105 new Callable<Response>() {
107 public Response call() {
108 return processExecuteQuery(content, versionParam, uri, queryFormat, subgraph, headers, info, req);
114 public Response processExecuteQuery(String content, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @DefaultValue("graphson") @QueryParam("format") String queryFormat,@DefaultValue("no_op") @QueryParam("subgraph") String subgraph, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) {
116 String methodName = "executeQuery";
117 String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId");
118 String realTime = headers.getRequestHeaders().getFirst("Real-Time");
119 String queryProcessor = headers.getRequestHeaders().getFirst("QueryProcessor");
120 QueryProcessorType processorType = this.processorType;
121 Response response = null;
122 TransactionalGraphEngine dbEngine = null;
124 LoggingContext.save();
125 this.checkQueryParams(info.getQueryParameters());
126 Format format = Format.valueOf(queryFormat);
127 if (queryProcessor != null) {
128 processorType = QueryProcessorType.valueOf(queryProcessor);
130 SubGraphStyle subGraphStyle = SubGraphStyle.valueOf(subgraph);
131 JsonParser parser = new JsonParser();
133 JsonObject input = parser.parse(content).getAsJsonObject();
135 JsonElement startElement = input.get("start");
136 JsonElement queryElement = input.get("query");
137 JsonElement gremlinElement = input.get("gremlin");
138 JsonElement dslElement = input.get("dsl");
139 List<URI> startURIs = new ArrayList<>();
140 String queryURI = "";
144 Version version = Version.valueOf(versionParam);
145 DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime);
146 HttpEntry httpEntry = new HttpEntry(version, introspectorFactoryType, queryStyle, type);
147 dbEngine = httpEntry.getDbEngine();
149 if (startElement != null) {
151 if (startElement.isJsonArray()) {
152 for (JsonElement element : startElement.getAsJsonArray()) {
153 startURIs.add(new URI(element.getAsString()));
156 startURIs.add(new URI(startElement.getAsString()));
159 if (queryElement != null) {
160 queryURI = queryElement.getAsString();
162 if (gremlinElement != null) {
163 gremlin = gremlinElement.getAsString();
165 if (dslElement != null) {
166 dsl = dslElement.getAsString();
168 URI queryURIObj = new URI(queryURI);
170 CustomQueryConfig customQueryConfig = getCustomQueryConfig(queryURIObj);
171 if ( customQueryConfig != null ) {
172 List<String> missingRequiredQueryParameters = checkForMissingQueryParameters( customQueryConfig.getQueryRequiredProperties(), URITools.getQueryMap(queryURIObj));
173 if ( !missingRequiredQueryParameters.isEmpty() ) {
174 return( createMessageMissingQueryRequiredParameters( missingRequiredQueryParameters, headers, info, req));
176 } else if ( queryElement != null ) {
177 return( createMessageInvalidQuerySection( queryURI, headers, info, req));
181 GenericQueryProcessor processor = null;
183 LoggingContext.targetEntity(TARGET_ENTITY);
184 LoggingContext.targetServiceName(methodName);
185 LoggingContext.startTime();
186 StopWatch.conditionalStart();
188 if (!startURIs.isEmpty()) {
189 Set<Vertex> vertexSet = new LinkedHashSet<>();
190 QueryParser uriQuery;
191 List<Vertex> vertices;
192 for (URI startUri : startURIs) {
193 uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(startUri, URITools.getQueryMap(startUri));
194 vertices = uriQuery.getQueryBuilder().toList();
195 vertexSet.addAll(vertices);
198 processor = new GenericQueryProcessor.Builder(dbEngine)
199 .startFrom(vertexSet).queryFrom(queryURIObj)
200 .processWith(processorType).create();
201 } else if (!queryURI.equals("")){
202 processor = new GenericQueryProcessor.Builder(dbEngine)
203 .queryFrom(queryURIObj)
204 .processWith(processorType).create();
205 } else if(!dsl.equals("")){
206 processor = new GenericQueryProcessor.Builder(dbEngine)
207 .queryFrom(dsl, "dsl")
208 .processWith(processorType).create();
210 processor = new GenericQueryProcessor.Builder(dbEngine)
211 .queryFrom(gremlin, "gremlin")
212 .processWith(processorType).create();
215 List<Object> vertices = processor.execute(subGraphStyle);
217 DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth);
218 FormatFactory ff = new FormatFactory(httpEntry.getLoader(), serializer);
220 Formatter formater = ff.get(format, info.getQueryParameters());
222 result = formater.output(vertices).toString();
224 double msecs = StopWatch.stopIfStarted();
225 LoggingContext.elapsedTime((long)msecs,TimeUnit.MILLISECONDS);
226 LoggingContext.successStatusFields();
227 LOGGER.info ("Completed");
230 response = Response.status(Status.OK)
231 .type(MediaType.APPLICATION_JSON)
232 .entity(result).build();
234 } catch (AAIException e) {
235 response = consumerExceptionResponseGenerator(headers, info, HttpMethod.GET, e);
236 } catch (Exception e ) {
237 AAIException ex = new AAIException("AAI_4000", e);
238 response = consumerExceptionResponseGenerator(headers, info, HttpMethod.GET, ex);
240 LoggingContext.restoreIfPossible();
241 LoggingContext.successStatusFields();
242 if (dbEngine != null) {
251 public void checkQueryParams(MultivaluedMap<String, String> params) throws AAIException {
253 if (params.containsKey(DEPTH) && params.getFirst(DEPTH).matches("\\d+")) {
254 String depth = params.getFirst(DEPTH);
255 Integer i = Integer.parseInt(depth);
257 throw new AAIException("AAI_3303");
264 private List<String> checkForMissingQueryParameters( List<String> requiredParameters, MultivaluedMap<String, String> queryParams ) {
265 List<String> result = new ArrayList<>();
267 for ( String param : requiredParameters ) {
268 if ( !queryParams.containsKey(param)) {
275 private CustomQueryConfig getCustomQueryConfig(URI uriObj ) {
277 GremlinServerSingleton gremlinServerSingleton;
278 CustomQueryConfig customQueryConfig;
279 String path = uriObj.getPath();
281 String[] parts = path.split("/");
282 boolean hasQuery = false;
283 for ( String part:parts ) {
285 gremlinServerSingleton = GremlinServerSingleton.getInstance();
286 return gremlinServerSingleton.getCustomQueryConfig(part);
288 if ( "query".equals(part)) {
297 private Response createMessageMissingQueryRequiredParameters(List<String> missingRequiredQueryParams, HttpHeaders headers, UriInfo info, HttpServletRequest req) {
298 AAIException e = new AAIException("AAI_3013");
300 ArrayList<String> templateVars = new ArrayList<>();
302 if (templateVars.isEmpty()) {
303 templateVars.add(missingRequiredQueryParams.toString());
306 Response response = Response
307 .status(e.getErrorObject().getHTTPResponseCode())
308 .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e,
309 templateVars)).build();
314 private Response createMessageInvalidQuerySection(String invalidQuery, HttpHeaders headers, UriInfo info, HttpServletRequest req) {
315 AAIException e = new AAIException("AAI_3014");
317 ArrayList<String> templateVars = new ArrayList<>();
319 if (templateVars.isEmpty()) {
320 templateVars.add(invalidQuery);
323 Response response = Response
324 .status(e.getErrorObject().getHTTPResponseCode())
325 .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e,
326 templateVars)).build();