Format Java code to ONAP standard
[aai/search-data-service.git] / src / main / java / org / onap / aai / sa / rest / DocumentApi.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 Amdocs
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
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
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=========================================================
20  */
21 package org.onap.aai.sa.rest;
22
23 import com.fasterxml.jackson.annotation.JsonInclude.Include;
24 import com.fasterxml.jackson.core.JsonProcessingException;
25 import com.fasterxml.jackson.databind.ObjectMapper;
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28 import org.onap.aai.cl.api.LogFields;
29 import org.onap.aai.cl.api.LogLine;
30 import org.onap.aai.cl.api.Logger;
31 import org.onap.aai.cl.eelf.LoggerFactory;
32 import org.onap.aai.sa.searchdbabstraction.elasticsearch.dao.DocumentStoreDataEntityImpl;
33 import org.onap.aai.sa.searchdbabstraction.elasticsearch.dao.DocumentStoreInterface;
34 import org.onap.aai.sa.searchdbabstraction.entity.AggregationResults;
35 import org.onap.aai.sa.searchdbabstraction.entity.DocumentOperationResult;
36 import org.onap.aai.sa.searchdbabstraction.entity.SearchOperationResult;
37 import org.onap.aai.sa.searchdbabstraction.logging.SearchDbMsgs;
38 import org.onap.aai.sa.searchdbabstraction.searchapi.SearchStatement;
39 import org.onap.aai.sa.searchdbabstraction.searchapi.SuggestionStatement;
40 import org.springframework.http.HttpHeaders;
41 import org.springframework.http.HttpStatus;
42 import org.springframework.http.MediaType;
43 import org.springframework.http.ResponseEntity;
44
45 public class DocumentApi {
46     private static final String REQUEST_HEADER_RESOURCE_VERSION = "If-Match";
47     private static final String RESPONSE_HEADER_RESOURCE_VERSION = "ETag";
48     private static final String REQUEST_HEADER_ALLOW_IMPLICIT_INDEX_CREATION = "X-CreateIndex";
49
50     protected SearchServiceApi searchService = null;
51
52     private Logger logger = LoggerFactory.getInstance().getLogger(DocumentApi.class.getName());
53     private Logger auditLogger = LoggerFactory.getInstance().getAuditLogger(DocumentApi.class.getName());
54
55     public DocumentApi(SearchServiceApi searchService) {
56         this.searchService = searchService;
57     }
58
59     public ResponseEntity<String> processPost(String content, HttpServletRequest request, HttpHeaders headers,
60             HttpServletResponse httpResponse, String index, DocumentStoreInterface documentStore) {
61
62         // Initialize the MDC Context for logging purposes.
63         ApiUtils.initMdcContext(request, headers);
64
65         try {
66             ObjectMapper mapper = new ObjectMapper();
67             mapper.setSerializationInclusion(Include.NON_EMPTY);
68             if (content == null) {
69                 return handleError(request, content, HttpStatus.BAD_REQUEST);
70             }
71
72             boolean isValid;
73             try {
74                 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.POST,
75                         ApiUtils.SEARCH_AUTH_POLICY_NAME);
76             } catch (Exception e) {
77                 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processPost", e.getMessage());
78                 return handleError(request, content, HttpStatus.FORBIDDEN);
79             }
80
81             if (!isValid) {
82                 return handleError(request, content, HttpStatus.FORBIDDEN);
83             }
84
85             DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl();
86             document.setContent(content);
87
88             DocumentOperationResult result =
89                     documentStore.createDocument(index, document, implicitlyCreateIndex(headers));
90             String output = null;
91             if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
92                 output = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getDocument());
93             } else {
94                 output = result.getError() != null
95                         ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
96                         : result.getFailureCause();
97             }
98
99             if (httpResponse != null) {
100                 httpResponse.setHeader(RESPONSE_HEADER_RESOURCE_VERSION, result.getResultVersion());
101             }
102             ResponseEntity response =
103                     ResponseEntity.status(result.getResultCode()).contentType(MediaType.APPLICATION_JSON).body(output);
104             logResult(request, HttpStatus.valueOf(response.getStatusCodeValue()));
105             logResult(request, HttpStatus.valueOf(response.getStatusCodeValue()));
106
107             // Clear the MDC context so that no other transaction inadvertently
108             // uses our transaction id.
109             ApiUtils.clearMdcContext();
110
111             return response;
112         } catch (Exception e) {
113             return handleError(request, e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
114         }
115     }
116
117     public ResponseEntity<String> processPut(String content, HttpServletRequest request, HttpHeaders headers,
118             HttpServletResponse httpResponse, String index, String id, DocumentStoreInterface documentStore) {
119
120         // Initialize the MDC Context for logging purposes.
121         ApiUtils.initMdcContext(request, headers);
122
123         try {
124             ObjectMapper mapper = new ObjectMapper();
125             mapper.setSerializationInclusion(Include.NON_EMPTY);
126             if (content == null) {
127                 return handleError(request, content, HttpStatus.BAD_REQUEST);
128             }
129
130             boolean isValid;
131             try {
132                 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.PUT,
133                         ApiUtils.SEARCH_AUTH_POLICY_NAME);
134             } catch (Exception e) {
135                 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processPut", e.getMessage());
136                 return handleError(request, content, HttpStatus.FORBIDDEN);
137             }
138
139             if (!isValid) {
140                 return handleError(request, content, HttpStatus.FORBIDDEN);
141             }
142
143             String resourceVersion = headers.getFirst(REQUEST_HEADER_RESOURCE_VERSION);
144
145             DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl();
146             document.setId(id);
147             document.setContent(content);
148             document.setVersion(resourceVersion);
149
150             DocumentOperationResult result = null;
151             if (resourceVersion == null) {
152                 result = documentStore.createDocument(index, document, implicitlyCreateIndex(headers));
153             } else {
154                 result = documentStore.updateDocument(index, document, implicitlyCreateIndex(headers));
155             }
156
157             String output = null;
158             if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
159                 output = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getDocument());
160             } else {
161                 output = result.getError() != null
162                         ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
163                         : result.getFailureCause();
164             }
165             if (httpResponse != null) {
166                 httpResponse.setHeader(RESPONSE_HEADER_RESOURCE_VERSION, result.getResultVersion());
167             }
168             ResponseEntity response =
169                     ResponseEntity.status(result.getResultCode()).contentType(MediaType.APPLICATION_JSON).body(output);
170             logResult(request, HttpStatus.valueOf(response.getStatusCodeValue()));
171
172             // Clear the MDC context so that no other transaction inadvertently
173             // uses our transaction id.
174             ApiUtils.clearMdcContext();
175
176             return response;
177         } catch (Exception e) {
178             return handleError(request, e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
179         }
180     }
181
182     public ResponseEntity<String> processDelete(String content, HttpServletRequest request, HttpHeaders headers,
183             HttpServletResponse httpResponse, String index, String id, DocumentStoreInterface documentStore) {
184
185         // Initialize the MDC Context for logging purposes.
186         ApiUtils.initMdcContext(request, headers);
187
188         try {
189             ObjectMapper mapper = new ObjectMapper();
190             mapper.setSerializationInclusion(Include.NON_EMPTY);
191             boolean isValid;
192             try {
193                 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.DELETE,
194                         ApiUtils.SEARCH_AUTH_POLICY_NAME);
195             } catch (Exception e) {
196                 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processDelete", e.getMessage());
197                 return handleError(request, content, HttpStatus.FORBIDDEN);
198             }
199
200             if (!isValid) {
201                 return handleError(request, content, HttpStatus.FORBIDDEN);
202             }
203
204             String resourceVersion = headers.getFirst(REQUEST_HEADER_RESOURCE_VERSION);
205             if (resourceVersion == null || resourceVersion.isEmpty()) {
206                 return handleError(request, "Request header 'If-Match' missing", HttpStatus.BAD_REQUEST);
207             }
208
209             DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl();
210             document.setId(id);
211             document.setVersion(resourceVersion);
212
213             DocumentOperationResult result = documentStore.deleteDocument(index, document);
214             String output = null;
215             if (!(result.getResultCode() >= 200 && result.getResultCode() <= 299)) { //
216                 output = result.getError() != null
217                         ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
218                         : result.getFailureCause();
219             }
220
221             if (httpResponse != null) {
222                 httpResponse.setHeader(RESPONSE_HEADER_RESOURCE_VERSION, result.getResultVersion());
223             }
224             ResponseEntity response;
225             if (output == null) {
226                 response = ResponseEntity.status(result.getResultCode()).build();
227             } else {
228                 response = ResponseEntity.status(result.getResultCode()).contentType(MediaType.APPLICATION_JSON)
229                         .body(output);
230             }
231
232             logResult(request, HttpStatus.valueOf(response.getStatusCodeValue()));
233
234             // Clear the MDC context so that no other transaction inadvertently
235             // uses our transaction id.
236             ApiUtils.clearMdcContext();
237
238             return response;
239         } catch (Exception e) {
240             return handleError(request, e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
241         }
242     }
243
244     public ResponseEntity<String> processGet(String content, HttpServletRequest request, HttpHeaders headers,
245             HttpServletResponse httpResponse, String index, String id, DocumentStoreInterface documentStore) {
246
247         // Initialize the MDC Context for logging purposes.
248         ApiUtils.initMdcContext(request, headers);
249
250         try {
251             ObjectMapper mapper = new ObjectMapper();
252             mapper.setSerializationInclusion(Include.NON_EMPTY);
253             boolean isValid;
254             try {
255                 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.GET,
256                         ApiUtils.SEARCH_AUTH_POLICY_NAME);
257             } catch (Exception e) {
258                 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processGet", e.getMessage());
259                 return handleError(request, content, HttpStatus.FORBIDDEN);
260             }
261
262             if (!isValid) {
263                 return handleError(request, content, HttpStatus.FORBIDDEN);
264             }
265
266             String resourceVersion = headers.getFirst(REQUEST_HEADER_RESOURCE_VERSION);
267
268             DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl();
269             document.setId(id);
270             document.setVersion(resourceVersion);
271
272             DocumentOperationResult result = documentStore.getDocument(index, document);
273             String output = null;
274             if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
275                 output = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getDocument());
276             } else {
277                 output = result.getError() != null
278                         ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
279                         : result.getFailureCause();
280             }
281             if (httpResponse != null) {
282                 httpResponse.setHeader(RESPONSE_HEADER_RESOURCE_VERSION, result.getResultVersion());
283             }
284             ResponseEntity response =
285                     ResponseEntity.status(result.getResultCode()).contentType(MediaType.APPLICATION_JSON).body(output);
286             logResult(request, HttpStatus.valueOf(response.getStatusCodeValue()));
287
288             // Clear the MDC context so that no other transaction inadvertently
289             // uses our transaction id.
290             ApiUtils.clearMdcContext();
291
292             return response;
293         } catch (Exception e) {
294             return handleError(request, e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
295         }
296     }
297
298     public ResponseEntity<String> processSearchWithGet(String content, HttpServletRequest request, HttpHeaders headers,
299             String index, String queryText, DocumentStoreInterface documentStore) {
300
301         // Initialize the MDC Context for logging purposes.
302         ApiUtils.initMdcContext(request, headers);
303
304         try {
305             ObjectMapper mapper = new ObjectMapper();
306             mapper.setSerializationInclusion(Include.NON_EMPTY);
307
308             boolean isValid;
309             try {
310                 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.GET,
311                         ApiUtils.SEARCH_AUTH_POLICY_NAME);
312             } catch (Exception e) {
313                 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "processSearchWithGet", e.getMessage());
314                 return handleError(request, content, HttpStatus.FORBIDDEN);
315             }
316
317             if (!isValid) {
318                 return handleError(request, content, HttpStatus.FORBIDDEN);
319             }
320
321             SearchOperationResult result = documentStore.search(index, queryText);
322             String output = null;
323             if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
324                 output = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getSearchResult());
325             } else {
326                 output = result.getError() != null
327                         ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
328                         : result.getFailureCause();
329             }
330             ResponseEntity response =
331                     ResponseEntity.status(result.getResultCode()).contentType(MediaType.APPLICATION_JSON).body(output);
332
333             // Clear the MDC context so that no other transaction inadvertently
334             // uses our transaction id.
335             ApiUtils.clearMdcContext();
336
337             return response;
338         } catch (Exception e) {
339             return handleError(request, e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
340         }
341     }
342
343     public ResponseEntity<String> queryWithGetWithPayload(String content, HttpServletRequest request,
344             HttpHeaders headers, String index, DocumentStoreInterface documentStore) {
345
346         // Initialize the MDC Context for logging purposes.
347         ApiUtils.initMdcContext(request, headers);
348
349         logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "GET",
350                 (request != null) ? request.getRequestURL().toString() : "");
351         if (logger.isDebugEnabled()) {
352             logger.debug("Request Body: " + content);
353         }
354         return processQuery(index, content, request, headers, documentStore);
355     }
356
357     public ResponseEntity<String> processSearchWithPost(String content, HttpServletRequest request, HttpHeaders headers,
358             String index, DocumentStoreInterface documentStore) {
359
360         // Initialize the MDC Context for logging purposes.
361         ApiUtils.initMdcContext(request, headers);
362
363         logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "POST",
364                 (request != null) ? request.getRequestURL().toString() : "");
365         if (logger.isDebugEnabled()) {
366             logger.debug("Request Body: " + content);
367         }
368
369         return processQuery(index, content, request, headers, documentStore);
370     }
371
372
373     public ResponseEntity<String> processSuggestQueryWithPost(String content, HttpServletRequest request,
374             HttpHeaders headers, String index, DocumentStoreInterface documentStore) {
375
376         // Initialize the MDC Context for logging purposes.
377         ApiUtils.initMdcContext(request, headers);
378
379         logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "POST",
380                 (request != null) ? request.getRequestURL().toString() : "");
381         if (logger.isDebugEnabled()) {
382             logger.debug("Request Body: " + content);
383         }
384
385         return processSuggestQuery(index, content, request, headers, documentStore);
386     }
387
388     /**
389      * Common handler for query requests. This is called by both the GET with payload and POST with payload variants of
390      * the query endpoint.
391      *
392      * @param index - The index to be queried against.
393      * @param content - The payload containing the query structure.
394      * @param request - The HTTP request.
395      * @param headers - The HTTP headers.
396      * @return - A standard HTTP response.
397      */
398     private ResponseEntity processQuery(String index, String content, HttpServletRequest request, HttpHeaders headers,
399             DocumentStoreInterface documentStore) {
400
401         try {
402             ObjectMapper mapper = new ObjectMapper();
403             mapper.setSerializationInclusion(Include.NON_EMPTY);
404
405             // Make sure that we were supplied a payload before proceeding.
406             if (content == null) {
407                 return handleError(request, content, HttpStatus.BAD_REQUEST);
408             }
409
410             // Validate that the request has the appropriate authorization.
411             boolean isValid;
412             try {
413                 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.POST,
414                         ApiUtils.SEARCH_AUTH_POLICY_NAME);
415
416             } catch (Exception e) {
417                 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "processQuery", e.getMessage());
418                 return handleError(request, content, HttpStatus.FORBIDDEN);
419             }
420
421             if (!isValid) {
422                 return handleError(request, content, HttpStatus.FORBIDDEN);
423             }
424
425             SearchStatement searchStatement;
426
427             try {
428                 // Marshall the supplied request payload into a search statement
429                 // object.
430                 searchStatement = mapper.readValue(content, SearchStatement.class);
431
432             } catch (Exception e) {
433                 return handleError(request, e.getMessage(), HttpStatus.BAD_REQUEST);
434             }
435
436             // Now, submit the search statement, translated into
437             // ElasticSearch syntax, to the document store DAO.
438             SearchOperationResult result = documentStore.searchWithPayload(index, searchStatement.toElasticSearch());
439             String output = null;
440             if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
441                 output = prepareOutput(mapper, result);
442             } else {
443                 output = result.getError() != null
444                         ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
445                         : result.getFailureCause();
446             }
447             ResponseEntity response =
448                     ResponseEntity.status(result.getResultCode()).contentType(MediaType.APPLICATION_JSON).body(output);
449
450             // Clear the MDC context so that no other transaction inadvertently
451             // uses our transaction id.
452             ApiUtils.clearMdcContext();
453
454             return response;
455
456         } catch (Exception e) {
457             return handleError(request, e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
458         }
459     }
460
461
462     /**
463      * Common handler for query requests. This is called by both the GET with payload and POST with payload variants of
464      * the query endpoint.
465      *
466      * @param index - The index to be queried against.
467      * @param content - The payload containing the query structure.
468      * @param request - The HTTP request.
469      * @param headers - The HTTP headers.
470      * @return - A standard HTTP response.
471      */
472     private ResponseEntity<String> processSuggestQuery(String index, String content, HttpServletRequest request,
473             HttpHeaders headers, DocumentStoreInterface documentStore) {
474
475         try {
476             ObjectMapper mapper = new ObjectMapper();
477             mapper.setSerializationInclusion(Include.NON_EMPTY);
478
479             // Make sure that we were supplied a payload before proceeding.
480             if (content == null) {
481                 return handleError(request, content, HttpStatus.BAD_REQUEST);
482             }
483
484             // Validate that the request has the appropriate authorization.
485             boolean isValid;
486             try {
487                 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.POST,
488                         ApiUtils.SEARCH_AUTH_POLICY_NAME);
489
490             } catch (Exception e) {
491                 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "processQuery", e.getMessage());
492                 return handleError(request, content, HttpStatus.FORBIDDEN);
493             }
494
495             if (!isValid) {
496                 return handleError(request, content, HttpStatus.FORBIDDEN);
497             }
498
499             SuggestionStatement suggestionStatement;
500
501             try {
502                 // Marshall the supplied request payload into a search statement
503                 // object.
504                 suggestionStatement = mapper.readValue(content, SuggestionStatement.class);
505
506             } catch (Exception e) {
507                 return handleError(request, e.getMessage(), HttpStatus.BAD_REQUEST);
508             }
509
510             // Now, submit the search statement, translated into
511             // ElasticSearch syntax, to the document store DAO.
512             SearchOperationResult result =
513                     documentStore.suggestionQueryWithPayload(index, suggestionStatement.toElasticSearch());
514             String output = null;
515             if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
516                 output = prepareSuggestOutput(mapper, result);
517             } else {
518                 output = result.getError() != null
519                         ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
520                         : result.getFailureCause();
521             }
522             ResponseEntity<String> response = ResponseEntity.status(result.getResultCode()).body(output);
523
524             // Clear the MDC context so that no other transaction inadvertently
525             // uses our transaction id.
526             ApiUtils.clearMdcContext();
527
528             return response;
529
530         } catch (Exception e) {
531             return handleError(request, e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
532         }
533     }
534
535     /**
536      * Checks the supplied HTTP headers to see if we should allow the underlying document store to implicitly create the
537      * index referenced in a document PUT or POST if it does not already exist in the data store.
538      *
539      * @param headers - The HTTP headers to examine.
540      *
541      * @return - true if the headers indicate that missing indices should be implicitly created, false otherwise.
542      */
543     private boolean implicitlyCreateIndex(HttpHeaders headers) {
544
545         boolean createIndexIfNotPresent = false;
546         String implicitIndexCreationHeader = headers.getFirst(REQUEST_HEADER_ALLOW_IMPLICIT_INDEX_CREATION);
547
548         if ((implicitIndexCreationHeader != null) && (implicitIndexCreationHeader.equals("true"))) {
549             createIndexIfNotPresent = true;
550         }
551
552         return createIndexIfNotPresent;
553     }
554
555     private String prepareOutput(ObjectMapper mapper, SearchOperationResult result) throws JsonProcessingException {
556         StringBuffer output = new StringBuffer();
557         output.append("{\r\n\"searchResult\":");
558         output.append(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getSearchResult()));
559         AggregationResults aggs = result.getAggregationResult();
560         if (aggs != null) {
561             output.append(",\r\n\"aggregationResult\":");
562             output.append(mapper.setSerializationInclusion(Include.NON_NULL).writerWithDefaultPrettyPrinter()
563                     .writeValueAsString(aggs));
564         }
565         output.append("\r\n}");
566         return output.toString();
567     }
568
569     private String prepareSuggestOutput(ObjectMapper mapper, SearchOperationResult result)
570             throws JsonProcessingException {
571         StringBuffer output = new StringBuffer();
572         output.append("{\r\n\"searchResult\":");
573         output.append(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getSuggestResult()));
574         AggregationResults aggs = result.getAggregationResult();
575         if (aggs != null) {
576             output.append(",\r\n\"aggregationResult\":");
577             output.append(mapper.setSerializationInclusion(Include.NON_NULL).writerWithDefaultPrettyPrinter()
578                     .writeValueAsString(aggs));
579         }
580         output.append("\r\n}");
581         return output.toString();
582     }
583
584     private ResponseEntity handleError(HttpServletRequest request, String message, HttpStatus status) {
585         logResult(request, status);
586         return ResponseEntity.status(status).contentType(MediaType.APPLICATION_JSON).body(message);
587     }
588
589     void logResult(HttpServletRequest request, HttpStatus status) {
590
591         logger.info(SearchDbMsgs.PROCESS_REST_REQUEST, (request != null) ? request.getMethod().toString() : "",
592                 (request != null) ? request.getRequestURL().toString() : "",
593                 (request != null) ? request.getRemoteHost() : "", Integer.toString(status.value()));
594
595         auditLogger.info(SearchDbMsgs.PROCESS_REST_REQUEST,
596                 new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, status.value())
597                         .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, status.getReasonPhrase()),
598                 (request != null) ? request.getMethod().toString() : "",
599                 (request != null) ? request.getRequestURL().toString() : "",
600                 (request != null) ? request.getRemoteHost() : "", Integer.toString(status.value()));
601
602         // Clear the MDC context so that no other transaction inadvertently
603         // uses our transaction id.
604         ApiUtils.clearMdcContext();
605     }
606 }