Spring-boot 3.1 update
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / rest / db / HttpEntry.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright © 2024 Deutsche Telekom.
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.rest.db;
24
25 import com.fasterxml.jackson.databind.JsonNode;
26 import com.fasterxml.jackson.databind.ObjectMapper;
27 import com.github.fge.jsonpatch.JsonPatchException;
28 import com.github.fge.jsonpatch.mergepatch.JsonMergePatch;
29
30 import java.io.IOException;
31 import java.io.UnsupportedEncodingException;
32 import java.lang.reflect.InvocationTargetException;
33 import java.net.MalformedURLException;
34 import java.net.URI;
35 import java.net.URISyntaxException;
36 import java.util.*;
37 import java.util.stream.Collectors;
38
39 import jakarta.ws.rs.core.*;
40 import jakarta.ws.rs.core.Response.Status;
41
42 import org.apache.tinkerpop.gremlin.structure.Vertex;
43 import org.janusgraph.core.JanusGraphException;
44 import org.javatuples.Pair;
45 import org.onap.aai.aailog.logs.AaiDBMetricLog;
46 import org.onap.aai.db.props.AAIProperties;
47 import org.onap.aai.exceptions.AAIException;
48 import org.onap.aai.introspection.*;
49 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
50 import org.onap.aai.introspection.sideeffect.OwnerCheck;
51 import org.onap.aai.logging.ErrorLogHelper;
52 import org.onap.aai.nodes.NodeIngestor;
53 import org.onap.aai.parsers.query.QueryParser;
54 import org.onap.aai.prevalidation.ValidationService;
55 import org.onap.aai.query.builder.QueryOptions;
56 import org.onap.aai.query.entities.PaginationResult;
57 import org.onap.aai.rest.notification.NotificationService;
58 import org.onap.aai.rest.notification.UEBNotification;
59 import org.onap.aai.restcore.HttpMethod;
60 import org.onap.aai.schema.enums.ObjectMetadata;
61 import org.onap.aai.serialization.db.DBSerializer;
62 import org.onap.aai.serialization.engines.JanusGraphDBEngine;
63 import org.onap.aai.serialization.engines.QueryStyle;
64 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
65 import org.onap.aai.serialization.engines.query.QueryEngine;
66 import org.onap.aai.serialization.queryformats.Format;
67 import org.onap.aai.serialization.queryformats.FormatFactory;
68 import org.onap.aai.serialization.queryformats.Formatter;
69 import org.onap.aai.setup.SchemaVersion;
70 import org.onap.aai.setup.SchemaVersions;
71 import org.onap.aai.transforms.XmlFormatTransformer;
72 import org.onap.aai.util.AAIConfig;
73 import org.onap.aai.util.AAIConstants;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76 import org.springframework.beans.factory.annotation.Autowired;
77 import org.springframework.beans.factory.annotation.Value;
78
79 /**
80  * The Class HttpEntry.
81  */
82 public class HttpEntry {
83
84     private static final Logger LOGGER = LoggerFactory.getLogger(HttpEntry.class);
85
86     private ModelType introspectorFactoryType;
87     private QueryStyle queryStyle;
88     private SchemaVersion version;
89     private Loader loader;
90     private TransactionalGraphEngine dbEngine;
91
92     @Autowired
93     private NodeIngestor nodeIngestor;
94
95     @Autowired
96     private LoaderFactory loaderFactory;
97
98     @Autowired
99     private SchemaVersions schemaVersions;
100
101     @Autowired
102     private NotificationService notificationService;
103
104     @Value("${schema.uri.base.path}")
105     private String basePath;
106
107     private String serverBase;
108
109     @Autowired
110     private XmlFormatTransformer xmlFormatTransformer;
111
112     private UEBNotification notification;
113
114     private int notificationDepth;
115
116     /**
117      * Instantiates a new http entry.
118      *
119      * @param modelType the model type
120      * @param queryStyle the query style
121      */
122     public HttpEntry(ModelType modelType, QueryStyle queryStyle) {
123         this.introspectorFactoryType = modelType;
124         this.queryStyle = queryStyle;
125     }
126
127     public HttpEntry setHttpEntryProperties(SchemaVersion version) {
128         this.version = version;
129         this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version);
130         this.dbEngine = new JanusGraphDBEngine(queryStyle, loader);
131
132         getDbEngine().startTransaction();
133         this.notification = new UEBNotification(loaderFactory, schemaVersions);
134         if ("true".equals(AAIConfig.get("aai.notification.depth.all.enabled", "true"))) {
135             this.notificationDepth = AAIProperties.MAXIMUM_DEPTH;
136         } else {
137             this.notificationDepth = AAIProperties.MINIMUM_DEPTH;
138         }
139         return this;
140     }
141
142     public HttpEntry setHttpEntryProperties(SchemaVersion version, String serverBase) {
143         setHttpEntryProperties(version);
144         this.serverBase = serverBase;
145         return this;
146     }
147
148     public HttpEntry setHttpEntryProperties(SchemaVersion version, UEBNotification notification) {
149         setHttpEntryProperties(version);
150         this.notification = notification;
151         return this;
152     }
153
154     public HttpEntry setHttpEntryProperties(SchemaVersion version, UEBNotification notification,
155             int notificationDepth) {
156         setHttpEntryProperties(version);
157         this.notification = notification;
158         this.notificationDepth = notificationDepth;
159         return this;
160     }
161
162     public ModelType getIntrospectorFactoryType() {
163         return introspectorFactoryType;
164     }
165
166     public QueryStyle getQueryStyle() {
167         return queryStyle;
168     }
169
170     public SchemaVersion getVersion() {
171         return version;
172     }
173
174     public Loader getLoader() {
175         return loader;
176     }
177
178     public TransactionalGraphEngine getDbEngine() {
179         return dbEngine;
180     }
181
182     public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth) throws AAIException {
183         return this.process(requests, sourceOfTruth, true);
184     }
185
186     public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth,boolean enableResourceVersion) throws AAIException {
187         return this.process(requests, sourceOfTruth, Collections.emptySet(), enableResourceVersion, null);
188     }
189
190     public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth, Set<String> groups) throws AAIException {
191         return this.process(requests, sourceOfTruth, groups, true, null);
192     }
193
194
195     public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth,
196             Set<String> groups, boolean enableResourceVersion, QueryOptions queryOptions) throws AAIException {
197
198         DBSerializer serializer = null;
199         if (serverBase != null) {
200             serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth, groups,
201                     notificationDepth, serverBase);
202         } else {
203             serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth, groups,
204                     notificationDepth);
205         }
206
207         Set<Vertex> mainVertexesToNotifyOn = new LinkedHashSet<>();
208         AaiDBMetricLog metricLog = new AaiDBMetricLog(AAIConstants.AAI_RESOURCES_MS);
209
210         String outputMediaType = null;
211         if (requests != null && !requests.isEmpty()) {
212             HttpHeaders headers = requests.get(0).getHeaders();
213             outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
214         }
215
216         String transactionId = requests.get(0).getTransactionId();
217         boolean success = true;
218         QueryEngine queryEngine = dbEngine.getQueryEngine();
219         List<Pair<URI, Response>> responses = new ArrayList<>();
220         for (DBRequest request : requests) {
221             Response response = null;
222             Status status = Status.NOT_FOUND;
223             HttpMethod method = request.getMethod();
224             metricLog.pre(request);
225             try {
226                 try {
227                     String uriTemp = request.getUri().getRawPath().replaceFirst("^v\\d+/", "");
228
229                     QueryParser query = request.getParser();
230                     List<Vertex> queryResult;
231                     PaginationResult<Vertex> paginationResult = null;
232                     if(queryOptions != null && queryOptions.getPageable() != null) {
233                         paginationResult = executePaginatedQuery(query, queryOptions);
234                         queryResult = paginationResult.getResults();
235                     } else {
236                         queryResult = executeQuery(query, queryOptions);
237                     }
238
239                     boolean groupsAvailable = serializer.getGroups() != null && !serializer.getGroups().isEmpty();
240                     List<Vertex> vertices = groupsAvailable
241                         ? queryResult.stream()
242                             .filter(vertex -> OwnerCheck.isAuthorized(groups, vertex))
243                             .collect(Collectors.toList())
244                         : queryResult;
245
246                     MultivaluedMap<String, String> params = request.getInfo().getQueryParameters(false);
247                     Introspector obj = request.getIntrospector();
248                     int depth = setDepth(obj, params.getFirst("depth"));
249                     Format format = null;
250                     if (params.containsKey("format")) {
251                         format = Format.getFormat(params.getFirst("format"));
252                     }
253
254                     String cleanUp = params.getFirst("cleanup");
255                     if (cleanUp == null) {
256                         cleanUp = "false";
257                     }
258                     if (vertices.size() > 1
259                             && !(method.equals(HttpMethod.GET) || method.equals(HttpMethod.GET_RELATIONSHIP))) {
260                         if (method.equals(HttpMethod.DELETE)) {
261
262                             throw new AAIException("AAI_6138");
263                         } else {
264                             throw new AAIException("AAI_6137");
265                         }
266                     }
267                     boolean isNewVertex;
268                     if (method.equals(HttpMethod.PUT)) {
269                         String resourceVersion = obj.getValue(AAIProperties.RESOURCE_VERSION);
270                         if (vertices.isEmpty()) {
271                             if (enableResourceVersion) {
272                                 serializer.verifyResourceVersion("create", query.getResultType(), "", resourceVersion,
273                                         obj.getURI());
274                             }
275                             isNewVertex = true;
276                         } else {
277                             if (enableResourceVersion) {
278                                 serializer.verifyResourceVersion("update", query.getResultType(),
279                                         vertices.get(0).<String>property(AAIProperties.RESOURCE_VERSION).orElse(null),
280                                         resourceVersion, obj.getURI());
281                             }
282                             isNewVertex = false;
283                         }
284                     } else {
285                         if (vertices.isEmpty()) {
286                             String msg = createNotFoundMessage(query.getResultType(), request.getUri());
287                             throw new AAIException("AAI_6114", msg);
288                         } else {
289                             isNewVertex = false;
290                         }
291                     }
292                     Vertex v = null;
293                     if (!isNewVertex) {
294                         v = vertices.get(0);
295                     }
296
297                     /*
298                      * This skip-related-to query parameter is used to determine if the relationships object will omit
299                      * the related-to-property
300                      * If a GET is sent to resources without a format, if format=resource, or if format=resource_and_url
301                      * with this param set to false
302                      * then behavior will be keep the related-to properties. By default, set to true.
303                      * Otherwise, for any other case, when the skip-related-to parameter exists, has value=true, or some
304                      * unfamiliar input (e.g. skip-related-to=bogusvalue), the value is true.
305                      */
306                     boolean isSkipRelatedTo = true;
307                     if (params.containsKey("skip-related-to")) {
308                         String skipRelatedTo = params.getFirst("skip-related-to");
309                         isSkipRelatedTo = !(skipRelatedTo != null && skipRelatedTo.equals("false"));
310                     } else {
311                         // if skip-related-to param is missing, then default it to false;
312                         isSkipRelatedTo = false;
313                     }
314
315                     HashMap<String, Introspector> relatedObjects = new HashMap<>();
316                     String nodeOnly = params.getFirst("nodes-only");
317                     boolean isNodeOnly = nodeOnly != null;
318
319                     String requestContext = "";
320                     List<String> requestContextList = request.getHeaders().getRequestHeader("aai-request-context");
321                     if (requestContextList != null) {
322                         requestContext = requestContextList.get(0);
323                     }
324                     URI uri = UriBuilder.fromPath(uriTemp).build();
325                     HttpHeaders headers = request.getHeaders();
326                     outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
327                     String result = null;
328                     switch (method) {
329                         case GET:
330
331                             if (format == null) {
332                                 obj = this.getObjectFromDb(vertices, serializer, query, obj, request.getUri(), depth,
333                                         isNodeOnly, cleanUp, isSkipRelatedTo);
334
335                                 if (obj != null) {
336                                     status = Status.OK;
337                                     MarshallerProperties properties;
338                                     Optional<MarshallerProperties> marshallerPropOpt =
339                                             request.getMarshallerProperties();
340                                     if (marshallerPropOpt.isPresent()) {
341                                         properties = marshallerPropOpt.get();
342                                     } else {
343                                         properties = new MarshallerProperties.Builder(
344                                                 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
345                                     }
346                                     result = obj.marshal(properties);
347                                 }
348                             } else {
349                                 FormatFactory ff = new FormatFactory(loader, serializer, schemaVersions, basePath + "/",
350                                         serverBase);
351                                 Formatter formatter = ff.get(format, params);
352                                 result = formatter.output(
353                                         vertices.stream().map(vertex -> (Object) vertex).collect(Collectors.toList()))
354                                         .toString();
355
356                                 if (outputMediaType == null) {
357                                     outputMediaType = MediaType.APPLICATION_JSON;
358                                 }
359
360                                 if (MediaType.APPLICATION_XML_TYPE.isCompatible(MediaType.valueOf(outputMediaType))) {
361                                     result = xmlFormatTransformer.transform(result);
362                                 }
363                                 status = Status.OK;
364                             }
365
366                             break;
367                         case GET_RELATIONSHIP:
368                             if (format == null) {
369                                 obj = this.getRelationshipObjectFromDb(vertices, serializer, query,
370                                         request.getInfo().getRequestUri(), isSkipRelatedTo);
371
372                                 if (obj != null) {
373                                     status = Status.OK;
374                                     MarshallerProperties properties;
375                                     if (request.getMarshallerProperties().isEmpty()) {
376                                         properties = new MarshallerProperties.Builder(
377                                                 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
378                                     } else {
379                                         properties = request.getMarshallerProperties().get();
380                                     }
381                                     result = obj.marshal(properties);
382                                 } else {
383                                     String msg =
384                                             createRelationshipNotFoundMessage(query.getResultType(), request.getUri());
385                                     throw new AAIException("AAI_6149", msg);
386                                 }
387                             } else {
388                                 FormatFactory ff = new FormatFactory(loader, serializer, schemaVersions, basePath + "/",
389                                         serverBase);
390                                 Formatter formatter = ff.get(format, params);
391                                 result = formatter.output(
392                                         vertices.stream().map(vertex -> (Object) vertex).collect(Collectors.toList()))
393                                         .toString();
394
395                                 if (outputMediaType == null) {
396                                     outputMediaType = MediaType.APPLICATION_JSON;
397                                 }
398
399                                 if (MediaType.APPLICATION_XML_TYPE.isCompatible(MediaType.valueOf(outputMediaType))) {
400                                     result = xmlFormatTransformer.transform(result);
401                                 }
402                                 status = Status.OK;
403                             }
404                             break;
405                         case PUT:
406                             if (isNewVertex) {
407                                 v = serializer.createNewVertex(obj);
408                             }
409                             serializer.serializeToDb(obj, v, query, uri.getRawPath(), requestContext);
410                             status = Status.OK;
411                             if (isNewVertex) {
412                                 status = Status.CREATED;
413                             }
414
415                             mainVertexesToNotifyOn.add(v);
416                             if (notificationDepth == AAIProperties.MINIMUM_DEPTH) {
417                                 Map<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> allImpliedDeleteObjs =
418                                         serializer.getImpliedDeleteUriObjectPair();
419
420                                 for (Map.Entry<String, Pair<Introspector, LinkedHashMap<String, Introspector>>> entry : allImpliedDeleteObjs
421                                         .entrySet()) {
422                                     // The format is purposefully %s/%s%s due to the fact
423                                     // that every aai-uri will have a slash at the beginning
424                                     // If that assumption isn't true, then its best to change this code
425                                     String curUri = "%s/%s%s".formatted(basePath, version, entry.getKey());
426                                     Introspector curObj = entry.getValue().getValue0();
427                                     HashMap<String, Introspector> curObjRelated = entry.getValue().getValue1();
428                                     notification.createNotificationEvent(transactionId, sourceOfTruth,
429                                             Status.NO_CONTENT, URI.create(curUri), curObj, curObjRelated, basePath);
430                                 }
431                             }
432
433                             break;
434                         case PUT_EDGE:
435                             serializer.touchStandardVertexProperties(v, false);
436                             Vertex relatedVertex = serializer.createEdge(obj, v);
437                             status = Status.OK;
438
439                             mainVertexesToNotifyOn.add(v);
440                             serializer.addVertexToEdgeVertexes(relatedVertex);
441                             break;
442                         case MERGE_PATCH:
443                             Introspector existingObj = loader.introspectorFromName(obj.getDbName());
444                             existingObj = this.getObjectFromDb(vertices, serializer, query, existingObj,
445                                     request.getUri(), 0, false, cleanUp);
446                             String existingJson = existingObj.marshal(false);
447                             String newJson;
448
449                             if (request.getRawRequestContent().isPresent()) {
450                                 newJson = request.getRawRequestContent().get();
451                             } else {
452                                 newJson = "";
453                             }
454                             Object relationshipList = request.getIntrospector().getValue("relationship-list");
455                             ObjectMapper mapper = new ObjectMapper();
456                             try {
457                                 JsonNode existingNode = mapper.readTree(existingJson);
458                                 JsonNode newNode = mapper.readTree(newJson);
459                                 JsonMergePatch patch = JsonMergePatch.fromJson(newNode);
460                                 JsonNode completed = patch.apply(existingNode);
461                                 String patched = mapper.writeValueAsString(completed);
462                                 Introspector patchedObj = loader.unmarshal(existingObj.getName(), patched);
463                                 if (relationshipList == null && patchedObj.hasProperty("relationship-list")) {
464                                     // if the caller didn't touch the relationship-list, we shouldn't either
465                                     patchedObj.setValue("relationship-list", null);
466                                 }
467                                 serializer.serializeToDb(patchedObj, v, query, uri.getRawPath(), requestContext);
468                                 status = Status.OK;
469                                 mainVertexesToNotifyOn.add(v);
470                             } catch (IOException | JsonPatchException e) {
471                                 throw new AAIException("AAI_3000", "could not perform patch operation");
472                             }
473                             break;
474                         case DELETE:
475                             String resourceVersion = params.getFirst(AAIProperties.RESOURCE_VERSION);
476                             obj = serializer.getLatestVersionView(v, notificationDepth);
477                             if (query.isDependent()) {
478                                 relatedObjects = serializer.getRelatedObjects(queryEngine, v, obj, this.loader);
479                             }
480                             /*
481                              * Find all Delete-other-vertex vertices and create structure for notify
482                              * findDeleatble also returns the startVertex v and we dont want to create
483                              * duplicate notification events for the same
484                              * So remove the startvertex first
485                              */
486
487                             List<Vertex> deletableVertices = dbEngine.getQueryEngine().findDeletable(v);
488                             Object vId = v.id();
489
490                             /*
491                              * I am assuming vertexId cant be null
492                              */
493                             deletableVertices.removeIf(s -> vId.equals(s.id()));
494                             boolean isDelVerticesPresent = !deletableVertices.isEmpty();
495                             Map<Vertex, Introspector> deleteObjects = new HashMap<>();
496                             Map<String, URI> uriMap = new HashMap<>();
497                             Map<String, HashMap<String, Introspector>> deleteRelatedObjects = new HashMap<>();
498
499                             if (isDelVerticesPresent) {
500                                 deleteObjects = this.buildIntrospectorObjects(serializer, deletableVertices);
501
502                                 uriMap = this.buildURIMap(serializer, deleteObjects);
503                                 deleteRelatedObjects = this.buildRelatedObjects(serializer, queryEngine, deleteObjects);
504                             }
505
506                             serializer.delete(v, deletableVertices, resourceVersion, enableResourceVersion);
507                             status = Status.NO_CONTENT;
508                             notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj,
509                                     relatedObjects, basePath);
510
511                             /*
512                              * Notify delete-other-v candidates
513                              */
514
515                             if (isDelVerticesPresent) {
516                                 notificationService.buildNotificationEvent(sourceOfTruth, status, transactionId, notification,
517                                         deleteObjects, uriMap, deleteRelatedObjects, basePath);
518                             }
519                             break;
520                         case DELETE_EDGE:
521                             serializer.touchStandardVertexProperties(v, false);
522                             Optional<Vertex> otherV = serializer.deleteEdge(obj, v);
523
524                             status = Status.NO_CONTENT;
525                             if (otherV.isPresent()) {
526                                 mainVertexesToNotifyOn.add(v);
527                                 serializer.addVertexToEdgeVertexes(otherV.get());
528                             }
529                             break;
530                         default:
531                             break;
532                     }
533
534                     /*
535                      * temporarily adding vertex id to the headers
536                      * to be able to use for testing the vertex id endpoint functionality
537                      * since we presently have no other way of generating those id urls
538                      */
539                     if (response == null && v != null && (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.GET)
540                             || method.equals(HttpMethod.MERGE_PATCH) || method.equals(HttpMethod.GET_RELATIONSHIP))
541
542                     ) {
543                         String myvertid = v.id().toString();
544                         if (paginationResult != null && paginationResult.getTotalCount() != null) {
545                             long totalPages = getTotalPages(queryOptions, paginationResult);
546                             response = Response.status(status).header("vertex-id", myvertid)
547                                     .header("total-results", paginationResult.getTotalCount())
548                                     .header("total-pages", totalPages)
549                                     .entity(result)
550                                     .type(outputMediaType).build();
551                         } else {
552                             response = Response.status(status).header("vertex-id", myvertid).entity(result)
553                                     .type(outputMediaType).build();
554                         }
555                     } else if (response == null) {
556                         response = Response.status(status).type(outputMediaType).build();
557                     } // else, response already set to something
558
559                     Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
560                     responses.add(pairedResp);
561                 } catch (JanusGraphException e) {
562                     this.dbEngine.rollback();
563                     throw new AAIException("AAI_6134", e);
564                 }
565             } catch (AAIException e) {
566                 success = false;
567                 ArrayList<String> templateVars = new ArrayList<>();
568                 templateVars.add(request.getMethod().toString()); // GET, PUT, etc
569                 templateVars.add(request.getUri().getPath());
570                 templateVars.addAll(e.getTemplateVars());
571                 ErrorLogHelper.logException(e);
572                 response =
573                         Response.status(e.getErrorObject().getHTTPResponseCode())
574                                 .entity(ErrorLogHelper.getRESTAPIErrorResponse(
575                                         request.getHeaders().getAcceptableMediaTypes(), e, templateVars))
576                                 .type(outputMediaType).build();
577                 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
578                 responses.add(pairedResp);
579             } catch (Exception e) {
580                 success = false;
581                 AAIException ex = new AAIException("AAI_4000", e);
582                 ArrayList<String> templateVars = new ArrayList<>();
583                 templateVars.add(request.getMethod().toString()); // GET, PUT, etc
584                 templateVars.add(request.getUri().getPath());
585                 ErrorLogHelper.logException(ex);
586                 response =
587                         Response.status(ex.getErrorObject().getHTTPResponseCode())
588                                 .entity(ErrorLogHelper.getRESTAPIErrorResponse(
589                                         request.getHeaders().getAcceptableMediaTypes(), ex, templateVars))
590                                 .type(outputMediaType).build();
591                 Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response);
592                 responses.add(pairedResp);
593             } finally {
594                 if (response != null) {
595                     metricLog.post(request, response);
596                 }
597             }
598         }
599
600         if (success) {
601             notificationService.generateEvents(notification, notificationDepth, sourceOfTruth, serializer, transactionId, queryEngine, mainVertexesToNotifyOn, version);
602         } else {
603             notification.clearEvents();
604         }
605
606         return Pair.with(success, responses);
607     }
608
609     private long getTotalPages(QueryOptions queryOptions, PaginationResult<Vertex> paginationResult) {
610         long totalCount = paginationResult.getTotalCount();
611         int pageSize = queryOptions.getPageable().getPageSize();
612         long totalPages = totalCount / pageSize;
613         // conditionally add a page for the remainder
614         if (totalCount % pageSize > 0) {
615             totalPages++;
616         }
617         return totalPages;
618     }
619
620     private List<Vertex> executeQuery(QueryParser query, QueryOptions queryOptions) {
621         return (queryOptions != null && queryOptions.getSort() != null)
622             ? query.getQueryBuilder().sort(queryOptions.getSort()).toList()
623             : query.getQueryBuilder().toList();
624     }
625
626     private PaginationResult<Vertex> executePaginatedQuery(QueryParser query, QueryOptions queryOptions) {
627         return queryOptions.getSort() != null
628             ? query.getQueryBuilder().sort(queryOptions.getSort()).toPaginationResult(queryOptions.getPageable())
629             : query.getQueryBuilder().toPaginationResult(queryOptions.getPageable());
630     }
631
632     private String getMediaType(List<MediaType> mediaTypeList) {
633         String mediaType = MediaType.APPLICATION_JSON; // json is the default
634         for (MediaType mt : mediaTypeList) {
635             if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
636                 mediaType = MediaType.APPLICATION_XML;
637             }
638         }
639         return mediaType;
640     }
641
642     private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
643             Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp)
644             throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
645             SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException,
646             AAIUnknownObjectException, URISyntaxException {
647
648         // nothing found
649         if (results.isEmpty()) {
650             String msg = createNotFoundMessage(query.getResultType(), uri);
651             throw new AAIException("AAI_6114", msg);
652         }
653
654         return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp);
655
656     }
657
658     private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
659             Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp, boolean isSkipRelatedTo)
660             throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
661             SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException,
662             AAIUnknownObjectException, URISyntaxException {
663
664         // nothing found
665         if (results.isEmpty()) {
666             String msg = createNotFoundMessage(query.getResultType(), uri);
667             throw new AAIException("AAI_6114", msg);
668         }
669
670         return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp, isSkipRelatedTo);
671
672     }
673
674     private Introspector getRelationshipObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
675             URI uri, boolean isSkipRelatedTo) throws AAIException, IllegalArgumentException, SecurityException,
676             UnsupportedEncodingException, AAIUnknownObjectException {
677
678         // nothing found
679         if (results.isEmpty()) {
680             String msg = createNotFoundMessage(query.getResultType(), uri);
681             throw new AAIException("AAI_6114", msg);
682         }
683
684         if (results.size() > 1) {
685             throw new AAIException("AAI_6148", uri.getPath());
686         }
687
688         Vertex v = results.get(0);
689         return serializer.dbToRelationshipObject(v, isSkipRelatedTo);
690     }
691
692     private String createNotFoundMessage(String resultType, URI uri) {
693         return "No Node of type " + resultType + " found at: " + uri.getPath();
694     }
695
696     private String createRelationshipNotFoundMessage(String resultType, URI uri) {
697         return "No relationship found of type " + resultType + " at the given URI: " + uri.getPath()
698                 + "/relationship-list";
699     }
700
701     protected int setDepth(Introspector obj, String depthParam) throws AAIException {
702         int depth = AAIProperties.MAXIMUM_DEPTH;
703
704         String getAllRandomStr = AAIConfig.get("aai.rest.getall.depthparam", "");
705         if (getAllRandomStr != null && !getAllRandomStr.isEmpty() && getAllRandomStr.equals(depthParam)) {
706             return depth;
707         }
708
709         if (depthParam == null) {
710             if (this.version.compareTo(schemaVersions.getDepthVersion()) >= 0) {
711                 depth = 0;
712             }
713         } else {
714             if (!depthParam.isEmpty() && !"all".equals(depthParam)) {
715                 try {
716                     depth = Integer.parseInt(depthParam);
717                 } catch (Exception e) {
718                     throw new AAIException("AAI_4016");
719                 }
720
721             }
722         }
723         String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH);
724
725         int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
726
727         if (maxDepth != null) {
728             try {
729                 maximumDepth = Integer.parseInt(maxDepth);
730             } catch (Exception ex) {
731                 throw new AAIException("AAI_4018");
732             }
733         }
734
735         if (depth > maximumDepth) {
736             throw new AAIException("AAI_3303");
737         }
738
739         return depth;
740     }
741
742     private Map<Vertex, Introspector> buildIntrospectorObjects(DBSerializer serializer, Iterable<Vertex> vertices) {
743         Map<Vertex, Introspector> deleteObjectMap = new HashMap<>();
744         for (Vertex vertex : vertices) {
745             try {
746                 Introspector deleteObj = serializer.getLatestVersionView(vertex, notificationDepth);
747                 deleteObjectMap.put(vertex, deleteObj);
748             } catch (UnsupportedEncodingException | AAIException e) {
749                 LOGGER.warn("Unable to get Introspctor Objects, Just continue");
750             }
751
752         }
753
754         return deleteObjectMap;
755
756     }
757
758     private Map<String, URI> buildURIMap(DBSerializer serializer, Map<Vertex, Introspector> introSpector) {
759         Map<String, URI> uriMap = new HashMap<>();
760         for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
761             URI uri;
762             try {
763                 uri = serializer.getURIForVertex(entry.getKey());
764                 if (null != entry.getValue()) {
765                     uriMap.put(entry.getValue().getObjectId(), uri);
766                 }
767             } catch (UnsupportedEncodingException e) {
768                 LOGGER.warn("Unable to get URIs, Just continue");
769             }
770
771         }
772
773         return uriMap;
774
775     }
776
777     private Map<String, HashMap<String, Introspector>> buildRelatedObjects(DBSerializer serializer,
778             QueryEngine queryEngine, Map<Vertex, Introspector> introSpector) {
779
780         Map<String, HashMap<String, Introspector>> relatedObjectsMap = new HashMap<>();
781         for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) {
782             try {
783                 HashMap<String, Introspector> relatedObjects =
784                         serializer.getRelatedObjects(queryEngine, entry.getKey(), entry.getValue(), this.loader);
785                 if (null != entry.getValue()) {
786                     relatedObjectsMap.put(entry.getValue().getObjectId(), relatedObjects);
787                 }
788             } catch (IllegalArgumentException | SecurityException | UnsupportedEncodingException | AAIException e) {
789                 LOGGER.warn("Unable to get realted Objects, Just continue");
790             }
791
792         }
793
794         return relatedObjectsMap;
795
796     }
797 }