2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2023 Deutsche Telekom SA.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
21 package org.onap.aai.rest;
23 import java.io.FileNotFoundException;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
29 import java.util.Objects;
30 import java.util.Optional;
32 import java.util.stream.Collectors;
34 import javax.servlet.http.HttpServletRequest;
35 import javax.ws.rs.core.MultivaluedHashMap;
36 import javax.ws.rs.core.MultivaluedMap;
38 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
39 import org.onap.aai.edges.EdgeIngestor;
40 import org.onap.aai.exceptions.AAIException;
41 import org.onap.aai.introspection.LoaderFactory;
42 import org.onap.aai.introspection.ModelType;
43 import org.onap.aai.rest.db.HttpEntry;
44 import org.onap.aai.rest.dsl.DslQueryProcessor;
45 import org.onap.aai.rest.dsl.V1DslQueryProcessor;
46 import org.onap.aai.rest.dsl.V2DslQueryProcessor;
47 import org.onap.aai.rest.dsl.v1.DslListener;
48 import org.onap.aai.rest.enums.QueryVersion;
49 import org.onap.aai.rest.search.GenericQueryProcessor;
50 import org.onap.aai.rest.search.GremlinServerSingleton;
51 import org.onap.aai.rest.search.QueryProcessorType;
52 import org.onap.aai.serialization.db.DBSerializer;
53 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
54 import org.onap.aai.serialization.queryformats.Format;
55 import org.onap.aai.serialization.queryformats.FormatFactory;
56 import org.onap.aai.serialization.queryformats.Formatter;
57 import org.onap.aai.serialization.queryformats.SubGraphStyle;
58 import org.onap.aai.setup.SchemaVersion;
59 import org.onap.aai.setup.SchemaVersions;
60 import org.onap.aai.transforms.XmlFormatTransformer;
61 import org.onap.aai.util.AAIConfig;
62 import org.onap.aai.util.TraversalConstants;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65 import org.springframework.beans.factory.annotation.Autowired;
66 import org.springframework.beans.factory.annotation.Qualifier;
67 import org.springframework.beans.factory.annotation.Value;
68 import org.springframework.http.HttpHeaders;
69 import org.springframework.http.MediaType;
70 import org.springframework.http.ResponseEntity;
71 import org.springframework.web.bind.annotation.PathVariable;
72 import org.springframework.web.bind.annotation.PutMapping;
73 import org.springframework.web.bind.annotation.RequestBody;
74 import org.springframework.web.bind.annotation.RequestHeader;
75 import org.springframework.web.bind.annotation.RequestMapping;
76 import org.springframework.web.bind.annotation.RequestParam;
77 import org.springframework.web.bind.annotation.RestController;
79 import com.google.gson.JsonElement;
80 import com.google.gson.JsonObject;
81 import com.google.gson.JsonParser;
83 import io.micrometer.core.annotation.Timed;
87 @RequestMapping("/{version:v[1-9][0-9]*|latest}/dsl")
88 public class DslConsumer extends TraversalConsumer {
90 private static final Logger LOGGER = LoggerFactory.getLogger(DslConsumer.class);
91 private static final QueryProcessorType processorType = QueryProcessorType.LOCAL_GROOVY;
92 private static final QueryVersion DEFAULT_VERSION = QueryVersion.V1;
94 private final HttpEntry httpEntry;
95 private final SchemaVersions schemaVersions;
96 private final String basePath;
97 private final GremlinServerSingleton gremlinServerSingleton;
98 private final XmlFormatTransformer xmlFormatTransformer;
99 private final DslListener v1DslListener;
100 private final org.onap.aai.rest.dsl.v2.DslListener v2DslListener;
102 private QueryVersion dslApiVersion = DEFAULT_VERSION;
103 Map<QueryVersion, DslQueryProcessor> dslQueryProcessors;
106 public DslConsumer(@Qualifier("requestScopedTraversalUriHttpEntry") HttpEntry requestScopedTraversalUriHttpEntry,
107 SchemaVersions schemaVersions, GremlinServerSingleton gremlinServerSingleton,
108 XmlFormatTransformer xmlFormatTransformer,
109 @Value("${schema.uri.base.path}") String basePath, DslListener v1DslListener, org.onap.aai.rest.dsl.v2.DslListener v2DslListener) {
110 this.httpEntry = requestScopedTraversalUriHttpEntry;
111 this.schemaVersions = schemaVersions;
112 this.gremlinServerSingleton = gremlinServerSingleton;
113 this.xmlFormatTransformer = xmlFormatTransformer;
114 this.basePath = basePath;
115 this.v1DslListener = v1DslListener;
116 this.v2DslListener = v2DslListener;
119 @PutMapping(produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
120 public ResponseEntity<String> executeQuery(@RequestBody String dslQuery,
121 @PathVariable("version") String versionParam,
122 @RequestParam(defaultValue = "graphson") String format,
123 @RequestParam(defaultValue = "no_op") String subgraph,
124 @RequestParam(defaultValue = "all") String validate,
125 @RequestParam(defaultValue = "-1") String resultIndex,
126 @RequestParam(defaultValue = "-1") String resultSize,
127 @RequestHeader HttpHeaders headers,
128 HttpServletRequest request) throws FileNotFoundException, AAIException {
129 Set<String> roles = this.getRoles(request.getUserPrincipal());
131 return processExecuteQuery(dslQuery, request, versionParam, format, subgraph,
132 validate, headers, resultIndex, resultSize, roles);
135 public ResponseEntity<String> processExecuteQuery(String dslQuery, HttpServletRequest request, String versionParam,
136 String queryFormat, String subgraph, String validate, HttpHeaders headers,
137 String resultIndex, String resultSize, Set<String> roles) throws FileNotFoundException, AAIException {
139 final SchemaVersion version = new SchemaVersion(versionParam);
140 final String sourceOfTruth = headers.getFirst("X-FromAppId");
141 final String dslOverride = headers.getFirst("X-DslOverride");
142 final MultivaluedMap<String,String> queryParams = toMultivaluedMap(request.getParameterMap());
144 Optional<String> dslApiVersionHeader =
145 Optional.ofNullable(headers.getFirst("X-DslApiVersion"));
146 if (dslApiVersionHeader.isPresent()) {
148 dslApiVersion = QueryVersion.valueOf(dslApiVersionHeader.get());
149 } catch (IllegalArgumentException e) {
150 LOGGER.debug("Defaulting DSL Api Version to " + DEFAULT_VERSION);
154 String result = executeQuery(dslQuery, request, queryFormat, subgraph, validate, queryParams, resultIndex, resultSize,
155 roles, version, sourceOfTruth, dslOverride);
156 MediaType acceptType = headers.getAccept().stream()
157 .filter(Objects::nonNull)
158 .filter(header -> !header.equals(MediaType.ALL))
160 .orElse(MediaType.APPLICATION_JSON);
162 if (MediaType.APPLICATION_XML.isCompatibleWith(acceptType)) {
163 result = xmlFormatTransformer.transform(result);
166 if (httpEntry.isPaginated()) {
167 return ResponseEntity.ok()
168 .header("total-results", String.valueOf(httpEntry.getTotalVertices()))
169 .header("total-pages", String.valueOf(httpEntry.getTotalPaginationBuckets()))
172 return ResponseEntity.ok(result);
176 private String executeQuery(String content, HttpServletRequest req, String queryFormat, String subgraph,
177 String validate, MultivaluedMap<String, String> queryParameters, String resultIndex, String resultSize, Set<String> roles,
178 final SchemaVersion version, final String sourceOfTruth, final String dslOverride)
179 throws AAIException, FileNotFoundException {
180 final String serverBase =
181 req.getRequestURL().toString().replaceAll("/(v[0-9]+|latest)/.*", "/");
182 httpEntry.setHttpEntryProperties(version, serverBase);
183 httpEntry.setPaginationParameters(resultIndex, resultSize);
185 JsonObject input = JsonParser.parseString(content).getAsJsonObject();
186 JsonElement dslElement = input.get("dsl");
188 if (dslElement != null) {
189 dsl = dslElement.getAsString();
192 boolean isDslOverride = dslOverride != null
193 && !AAIConfig.get(TraversalConstants.DSL_OVERRIDE).equals("false")
194 && dslOverride.equals(AAIConfig.get(TraversalConstants.DSL_OVERRIDE));
196 DslQueryProcessor dslQueryProcessor = dslApiVersion.equals(QueryVersion.V1)
197 ? new V1DslQueryProcessor()
198 : new V2DslQueryProcessor();
200 dslQueryProcessor.setStartNodeValidationFlag(false);
203 dslQueryProcessor.setValidationRules(validate);
205 Format format = Format.getFormat(queryFormat);
207 if (isAggregate(format)) {
208 dslQueryProcessor.setAggregate(true);
211 if (isHistory(format)) {
212 validateHistoryParams(format, queryParameters);
215 final TransactionalGraphEngine dbEngine = httpEntry.getDbEngine();
216 GraphTraversalSource traversalSource =
217 getTraversalSource(dbEngine, format, queryParameters, roles);
219 GenericQueryProcessor processor =
220 new GenericQueryProcessor.Builder(dbEngine, gremlinServerSingleton)
221 .queryFrom(dsl, "dsl").queryProcessor(dslQueryProcessor).version(dslApiVersion)
222 .processWith(processorType).format(format).uriParams(queryParameters)
223 .traversalSource(isHistory(format), traversalSource).create();
225 SubGraphStyle subGraphStyle = SubGraphStyle.valueOf(subgraph);
226 List<Object> vertTemp = processor.execute(subGraphStyle);
228 List<Object> vertices;
229 if (isAggregate(format)) {
230 // Dedup if duplicate objects are returned in each array in the aggregate format
232 List<Object> vertTempDedupedObjectList = dedupObjectInAggregateFormatResult(vertTemp);
234 .getPaginatedVertexListForAggregateFormat(vertTempDedupedObjectList);
236 vertices = httpEntry.getPaginatedVertexList(vertTemp);
239 DBSerializer serializer =
240 new DBSerializer(version, dbEngine, ModelType.MOXY, sourceOfTruth);
241 FormatFactory ff = new FormatFactory(httpEntry.getLoader(), serializer,
242 schemaVersions, this.basePath, serverBase);
244 MultivaluedMap<String, String> mvm = new MultivaluedHashMap<>();
245 mvm.putAll(queryParameters);
246 if (isHistory(format)) {
247 mvm.putSingle("startTs", Long.toString(getStartTime(format, mvm)));
248 mvm.putSingle("endTs", Long.toString(getEndTime(mvm)));
250 Formatter formatter = ff.get(format, mvm);
252 final Map<String, List<String>> propertiesMap = processor.getPropertiesMap();
254 if (propertiesMap != null && !propertiesMap.isEmpty()) {
255 result = formatter.output(vertices, propertiesMap).toString();
257 result = formatter.output(vertices).toString();
262 private List<Object> dedupObjectInAggregateFormatResultStreams(List<Object> vertTemp) {
263 return vertTemp.stream()
264 .filter(o -> o instanceof ArrayList)
265 .map(o -> ((ArrayList<?>) o).stream().distinct().collect(Collectors.toList()))
266 .collect(Collectors.toList());
268 private List<Object> dedupObjectInAggregateFormatResult(List<Object> vertTemp) {
269 List<Object> vertTempDedupedObjectList = new ArrayList<Object>();
270 Iterator<Object> itr = vertTemp.listIterator();
271 while (itr.hasNext()) {
272 Object o = itr.next();
273 if (o instanceof ArrayList) {
274 vertTempDedupedObjectList
275 .add(((ArrayList) o).stream().distinct().collect(Collectors.toList()));
278 return vertTempDedupedObjectList;
281 private MultivaluedMap<String, String> toMultivaluedMap(Map<String, String[]> map) {
282 MultivaluedMap<String, String> multivaluedMap = new MultivaluedHashMap<>();
284 for (Map.Entry<String, String[]> entry : map.entrySet()) {
285 for (String val : entry.getValue())
286 multivaluedMap.add(entry.getKey(), val);
289 return multivaluedMap;