Refactor global exception handler
[aai/traversal.git] / aai-traversal / src / main / java / org / onap / aai / rest / DslConsumer.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;
21
22 import java.util.ArrayList;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.stream.Collectors;
29
30 import javax.servlet.http.HttpServletRequest;
31 import javax.ws.rs.Consumes;
32 import javax.ws.rs.DefaultValue;
33 import javax.ws.rs.PUT;
34 import javax.ws.rs.Path;
35 import javax.ws.rs.PathParam;
36 import javax.ws.rs.Produces;
37 import javax.ws.rs.QueryParam;
38 import javax.ws.rs.core.Context;
39 import javax.ws.rs.core.HttpHeaders;
40 import javax.ws.rs.core.MediaType;
41 import javax.ws.rs.core.MultivaluedHashMap;
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;
46
47 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
48 import org.janusgraph.core.SchemaViolationException;
49 import org.onap.aai.concurrent.AaiCallable;
50 import org.onap.aai.exceptions.AAIException;
51 import org.onap.aai.introspection.ModelType;
52 import org.onap.aai.rest.db.HttpEntry;
53 import org.onap.aai.rest.dsl.DslQueryProcessor;
54 import org.onap.aai.rest.enums.QueryVersion;
55 import org.onap.aai.rest.search.GenericQueryProcessor;
56 import org.onap.aai.rest.search.GremlinServerSingleton;
57 import org.onap.aai.rest.search.QueryProcessorType;
58 import org.onap.aai.restcore.HttpMethod;
59 import org.onap.aai.serialization.db.DBSerializer;
60 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
61 import org.onap.aai.serialization.queryformats.Format;
62 import org.onap.aai.serialization.queryformats.FormatFactory;
63 import org.onap.aai.serialization.queryformats.Formatter;
64 import org.onap.aai.serialization.queryformats.SubGraphStyle;
65 import org.onap.aai.setup.SchemaVersion;
66 import org.onap.aai.setup.SchemaVersions;
67 import org.onap.aai.transforms.XmlFormatTransformer;
68 import org.onap.aai.util.AAIConfig;
69 import org.onap.aai.util.TraversalConstants;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72 import org.springframework.beans.factory.annotation.Autowired;
73 import org.springframework.beans.factory.annotation.Value;
74
75 import com.google.gson.JsonElement;
76 import com.google.gson.JsonObject;
77 import com.google.gson.JsonParser;
78
79 import io.micrometer.core.annotation.Timed;
80
81 @Path("{version: v[1-9][0-9]*|latest}/dsl")
82 @Timed
83 public class DslConsumer extends TraversalConsumer {
84
85     private HttpEntry traversalUriHttpEntry;
86
87     private QueryProcessorType processorType = QueryProcessorType.LOCAL_GROOVY;
88
89     private static final Logger LOGGER = LoggerFactory.getLogger(DslConsumer.class);
90
91     private DslQueryProcessor dslQueryProcessor;
92
93     private SchemaVersions schemaVersions;
94
95     private String basePath;
96
97     private GremlinServerSingleton gremlinServerSingleton;
98     private final QueryVersion DEFAULT_VERSION = QueryVersion.V1;
99     private QueryVersion dslApiVersion = DEFAULT_VERSION;
100
101     private XmlFormatTransformer xmlFormatTransformer;
102
103     @Autowired
104     public DslConsumer(HttpEntry traversalUriHttpEntry, DslQueryProcessor dslQueryProcessor,
105         SchemaVersions schemaVersions, GremlinServerSingleton gremlinServerSingleton,
106         XmlFormatTransformer xmlFormatTransformer,
107         @Value("${schema.uri.base.path}") String basePath) {
108         this.traversalUriHttpEntry = traversalUriHttpEntry;
109         this.dslQueryProcessor = dslQueryProcessor;
110         this.schemaVersions = schemaVersions;
111         this.gremlinServerSingleton = gremlinServerSingleton;
112         this.xmlFormatTransformer = xmlFormatTransformer;
113         this.basePath = basePath;
114     }
115
116     @PUT
117     @Consumes({MediaType.APPLICATION_JSON})
118     @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
119     public Response executeQuery(String content, @PathParam("version") String versionParam,
120         @DefaultValue("graphson") @QueryParam("format") String queryFormat,
121         @DefaultValue("no_op") @QueryParam("subgraph") String subgraph,
122         @DefaultValue("all") @QueryParam("validate") String validate, @Context HttpHeaders headers,
123         @Context HttpServletRequest req, @Context UriInfo info,
124         @DefaultValue("-1") @QueryParam("resultIndex") String resultIndex,
125         @DefaultValue("-1") @QueryParam("resultSize") String resultSize) {
126         Set<String> roles = this.getRoles(req.getUserPrincipal());
127
128         return runner(TraversalConstants.AAI_TRAVERSAL_DSL_TIMEOUT_ENABLED,
129             TraversalConstants.AAI_TRAVERSAL_DSL_TIMEOUT_APP,
130             TraversalConstants.AAI_TRAVERSAL_DSL_TIMEOUT_LIMIT, headers, info, HttpMethod.PUT,
131             new AaiCallable() {
132                 @Override
133                 public Response process() throws Exception {
134                     return (processExecuteQuery(content, req, versionParam, queryFormat, subgraph,
135                         validate, headers, info, resultIndex, resultSize, roles));
136                 }
137             });
138     }
139
140     public Response processExecuteQuery(String content, HttpServletRequest req, String versionParam,
141         String queryFormat, String subgraph, String validate, HttpHeaders headers, UriInfo info,
142         String resultIndex, String resultSize, Set<String> roles) {
143
144         String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId");
145         String dslOverride = headers.getRequestHeaders().getFirst("X-DslOverride");
146
147         Optional<String> dslApiVersionHeader =
148             Optional.ofNullable(headers.getRequestHeaders().getFirst("X-DslApiVersion"));
149         if (dslApiVersionHeader.isPresent()) {
150             try {
151                 dslApiVersion = QueryVersion.valueOf(dslApiVersionHeader.get());
152             } catch (IllegalArgumentException e) {
153                 LOGGER.debug("Defaulting DSL Api Version to  " + DEFAULT_VERSION);
154             }
155         }
156
157         Response response;
158         SchemaVersion version = new SchemaVersion(versionParam);
159
160         TransactionalGraphEngine dbEngine = null;
161         try {
162             String serverBase =
163                 req.getRequestURL().toString().replaceAll("/(v[0-9]+|latest)/.*", "/");
164             traversalUriHttpEntry.setHttpEntryProperties(version, serverBase);
165             traversalUriHttpEntry.setPaginationParameters(resultIndex, resultSize);
166             dbEngine = traversalUriHttpEntry.getDbEngine();
167             JsonObject input = JsonParser.parseString(content).getAsJsonObject();
168             JsonElement dslElement = input.get("dsl");
169             String dsl = "";
170             if (dslElement != null) {
171                 dsl = dslElement.getAsString();
172             }
173
174             boolean isDslOverride = dslOverride != null
175                 && !AAIConfig.get(TraversalConstants.DSL_OVERRIDE).equals("false")
176                 && dslOverride.equals(AAIConfig.get(TraversalConstants.DSL_OVERRIDE));
177
178             if (isDslOverride) {
179                 dslQueryProcessor.setStartNodeValidationFlag(false);
180             }
181
182             dslQueryProcessor.setValidationRules(validate);
183
184             Format format = Format.getFormat(queryFormat);
185
186             if (isAggregate(format)) {
187                 dslQueryProcessor.setAggregate(true);
188             }
189
190             if (isHistory(format)) {
191                 validateHistoryParams(format, info.getQueryParameters());
192             }
193
194             GraphTraversalSource traversalSource =
195                 getTraversalSource(dbEngine, format, info, roles);
196
197             GenericQueryProcessor processor =
198                 new GenericQueryProcessor.Builder(dbEngine, gremlinServerSingleton)
199                     .queryFrom(dsl, "dsl").queryProcessor(dslQueryProcessor).version(dslApiVersion)
200                     .processWith(processorType).format(format).uriParams(info.getQueryParameters())
201                     .traversalSource(isHistory(format), traversalSource).create();
202
203             SubGraphStyle subGraphStyle = SubGraphStyle.valueOf(subgraph);
204             List<Object> vertTemp = processor.execute(subGraphStyle);
205
206             // Dedup if duplicate objects are returned in each array in the aggregate format
207             // scenario.
208             List<Object> vertTempDedupedObjectList = dedupObjectInAggregateFormatResult(vertTemp);
209
210             List<Object> vertices;
211             if (isAggregate(format)) {
212                 vertices = traversalUriHttpEntry
213                     .getPaginatedVertexListForAggregateFormat(vertTempDedupedObjectList);
214             } else {
215                 vertices = traversalUriHttpEntry.getPaginatedVertexList(vertTemp);
216             }
217
218             DBSerializer serializer =
219                 new DBSerializer(version, dbEngine, ModelType.MOXY, sourceOfTruth);
220             FormatFactory ff = new FormatFactory(traversalUriHttpEntry.getLoader(), serializer,
221                 schemaVersions, this.basePath, serverBase);
222
223             MultivaluedMap<String, String> mvm = new MultivaluedHashMap<>();
224             mvm.putAll(info.getQueryParameters());
225             if (isHistory(format)) {
226                 mvm.putSingle("startTs", Long.toString(getStartTime(format, mvm)));
227                 mvm.putSingle("endTs", Long.toString(getEndTime(mvm)));
228             }
229             Formatter formatter = ff.get(format, mvm);
230
231             final Map<String, List<String>> propertiesMap = processor.getPropertiesMap();
232             String result = "";
233             if (propertiesMap != null && !propertiesMap.isEmpty()) {
234                 result = formatter.output(vertices, propertiesMap).toString();
235             } else {
236                 result = formatter.output(vertices).toString();
237             }
238
239             String acceptType = headers.getHeaderString("Accept");
240
241             if (acceptType == null) {
242                 acceptType = MediaType.APPLICATION_JSON;
243             }
244
245             if (MediaType.APPLICATION_XML_TYPE.isCompatible(MediaType.valueOf(acceptType))) {
246                 result = xmlFormatTransformer.transform(result);
247             }
248
249             if (traversalUriHttpEntry.isPaginated()) {
250                 response = Response.status(Status.OK).type(acceptType)
251                     .header("total-results", traversalUriHttpEntry.getTotalVertices())
252                     .header("total-pages", traversalUriHttpEntry.getTotalPaginationBuckets())
253                     .entity(result).build();
254             } else {
255                 response = Response.status(Status.OK).type(acceptType).entity(result).build();
256             }
257
258         } catch (AAIException e) {
259             response = consumerExceptionResponseGenerator(headers, info, HttpMethod.PUT, e);
260         } catch (SchemaViolationException sve) {
261             AAIException ex = new AAIException("AAI_4020", sve);
262             response = consumerExceptionResponseGenerator(headers, info, HttpMethod.PUT, ex);
263         } catch (Exception e) {
264             AAIException ex = new AAIException("AAI_4000", e);
265             response = consumerExceptionResponseGenerator(headers, info, HttpMethod.PUT, ex);
266         } finally {
267             if (dbEngine != null) {
268                 dbEngine.rollback();
269             }
270
271         }
272
273         return response;
274     }
275
276     private List<Object> dedupObjectInAggregateFormatResult(List<Object> vertTemp) {
277         List<Object> vertTempDedupedObjectList = new ArrayList<Object>();
278         Iterator<Object> itr = vertTemp.listIterator();
279         while (itr.hasNext()) {
280             Object o = itr.next();
281             if (o instanceof ArrayList) {
282                 vertTempDedupedObjectList
283                     .add(((ArrayList) o).stream().distinct().collect(Collectors.toList()));
284             }
285         }
286         return vertTempDedupedObjectList;
287     }
288 }