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