2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017 AT&T Intellectual Property.
6 * Copyright © 2017 Amdocs
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 ati
13 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
22 * ECOMP and OpenECOMP are trademarks
23 * and service marks of AT&T Intellectual Property.
25 package org.openecomp.sa.rest;
27 import com.fasterxml.jackson.annotation.JsonInclude.Include;
28 import com.fasterxml.jackson.core.JsonProcessingException;
29 import com.fasterxml.jackson.databind.ObjectMapper;
30 import org.openecomp.cl.api.LogFields;
31 import org.openecomp.cl.api.LogLine;
32 import org.openecomp.cl.api.Logger;
33 import org.openecomp.cl.eelf.LoggerFactory;
34 import org.openecomp.sa.searchdbabstraction.elasticsearch.dao.DocumentStoreDataEntityImpl;
35 import org.openecomp.sa.searchdbabstraction.elasticsearch.dao.DocumentStoreInterface;
36 import org.openecomp.sa.searchdbabstraction.entity.AggregationResults;
37 import org.openecomp.sa.searchdbabstraction.entity.DocumentOperationResult;
38 import org.openecomp.sa.searchdbabstraction.entity.SearchOperationResult;
39 import org.openecomp.sa.searchdbabstraction.logging.SearchDbMsgs;
40 import org.openecomp.sa.searchdbabstraction.searchapi.SearchStatement;
42 import javax.servlet.http.HttpServletRequest;
43 import javax.servlet.http.HttpServletResponse;
44 import javax.ws.rs.core.HttpHeaders;
45 import javax.ws.rs.core.MediaType;
46 import javax.ws.rs.core.Response;
47 import javax.ws.rs.core.Response.Status;
49 public class DocumentApi {
50 private static final String REQUEST_HEADER_RESOURCE_VERSION = "If-Match";
51 private static final String RESPONSE_HEADER_RESOURCE_VERSION = "ETag";
53 protected SearchServiceApi searchService = null;
55 private Logger logger = LoggerFactory.getInstance().getLogger(DocumentApi.class.getName());
56 private Logger auditLogger = LoggerFactory.getInstance()
57 .getAuditLogger(DocumentApi.class.getName());
59 public DocumentApi(SearchServiceApi searchService) {
60 this.searchService = searchService;
63 public Response processPost(String content, HttpServletRequest request, HttpHeaders headers,
64 HttpServletResponse httpResponse, String index,
65 DocumentStoreInterface documentStore) {
67 // Initialize the MDC Context for logging purposes.
68 ApiUtils.initMdcContext(request, headers);
71 ObjectMapper mapper = new ObjectMapper();
72 mapper.setSerializationInclusion(Include.NON_EMPTY);
73 if (content == null) {
74 return handleError(request, content, Status.BAD_REQUEST);
79 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.POST,
80 ApiUtils.SEARCH_AUTH_POLICY_NAME);
81 } catch (Exception e) {
82 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL,
83 "DocumentApi.processPost",
85 return handleError(request, content, Status.FORBIDDEN);
89 return handleError(request, content, Status.FORBIDDEN);
92 DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl();
93 document.setContent(content);
95 DocumentOperationResult result = documentStore.createDocument(index, document);
97 if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
98 output = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getDocument());
100 output = result.getError() != null
101 ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
102 : result.getFailureCause();
105 if (httpResponse != null) {
106 httpResponse.setHeader(RESPONSE_HEADER_RESOURCE_VERSION, result.getResultVersion());
108 Response response = Response.status(result.getResultCode()).entity(output).build();
109 logResult(request, Response.Status.fromStatusCode(response.getStatus()));
111 // Clear the MDC context so that no other transaction inadvertently
112 // uses our transaction id.
113 ApiUtils.clearMdcContext();
116 } catch (Exception e) {
117 return handleError(request, e.getMessage(), Status.INTERNAL_SERVER_ERROR);
121 public Response processPut(String content, HttpServletRequest request, HttpHeaders headers,
122 HttpServletResponse httpResponse, String index,
123 String id, DocumentStoreInterface documentStore) {
125 // Initialize the MDC Context for logging purposes.
126 ApiUtils.initMdcContext(request, headers);
129 ObjectMapper mapper = new ObjectMapper();
130 mapper.setSerializationInclusion(Include.NON_EMPTY);
131 if (content == null) {
132 return handleError(request, content, Status.BAD_REQUEST);
137 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.PUT,
138 ApiUtils.SEARCH_AUTH_POLICY_NAME);
139 } catch (Exception e) {
140 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL,
141 "DocumentApi.processPut",
143 return handleError(request, content, Status.FORBIDDEN);
147 return handleError(request, content, Status.FORBIDDEN);
150 String resourceVersion = headers.getRequestHeaders()
151 .getFirst(REQUEST_HEADER_RESOURCE_VERSION);
153 DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl();
155 document.setContent(content);
156 document.setVersion(resourceVersion);
158 DocumentOperationResult result = null;
159 if (resourceVersion == null) {
160 result = documentStore.createDocument(index, document);
162 result = documentStore.updateDocument(index, document);
165 String output = null;
166 if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
167 output = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getDocument());
169 output = result.getError() != null
170 ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
171 : result.getFailureCause();
173 if (httpResponse != null) {
174 httpResponse.setHeader(RESPONSE_HEADER_RESOURCE_VERSION, result.getResultVersion());
176 Response response = Response.status(result.getResultCode()).entity(output).build();
177 logResult(request, Response.Status.fromStatusCode(response.getStatus()));
179 // Clear the MDC context so that no other transaction inadvertently
180 // uses our transaction id.
181 ApiUtils.clearMdcContext();
184 } catch (Exception e) {
185 return handleError(request, e.getMessage(), Status.INTERNAL_SERVER_ERROR);
189 public Response processDelete(String content, HttpServletRequest request, HttpHeaders headers,
190 HttpServletResponse httpResponse, String index, String id,
191 DocumentStoreInterface documentStore) {
193 // Initialize the MDC Context for logging purposes.
194 ApiUtils.initMdcContext(request, headers);
197 ObjectMapper mapper = new ObjectMapper();
198 mapper.setSerializationInclusion(Include.NON_EMPTY);
201 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.DELETE,
202 ApiUtils.SEARCH_AUTH_POLICY_NAME);
203 } catch (Exception e) {
204 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL,
205 "DocumentApi.processDelete",
207 return handleError(request, content, Status.FORBIDDEN);
211 return handleError(request, content, Status.FORBIDDEN);
214 String resourceVersion = headers.getRequestHeaders()
215 .getFirst(REQUEST_HEADER_RESOURCE_VERSION);
216 if (resourceVersion == null || resourceVersion.isEmpty()) {
217 return handleError(request, "Request header 'If-Match' missing",
218 javax.ws.rs.core.Response.Status.BAD_REQUEST);
221 DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl();
223 document.setVersion(resourceVersion);
225 DocumentOperationResult result = documentStore.deleteDocument(index, document);
226 String output = null;
227 if (!(result.getResultCode() >= 200 && result.getResultCode() <= 299)) { //
228 output = result.getError() != null
229 ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
230 : result.getFailureCause();
233 if (httpResponse != null) {
234 httpResponse.setHeader(RESPONSE_HEADER_RESOURCE_VERSION, result.getResultVersion());
237 if (output == null) {
238 response = Response.status(result.getResultCode()).build();
240 response = Response.status(result.getResultCode()).entity(output).build();
243 logResult(request, Response.Status.fromStatusCode(response.getStatus()));
245 // Clear the MDC context so that no other transaction inadvertently
246 // uses our transaction id.
247 ApiUtils.clearMdcContext();
250 } catch (Exception e) {
251 return handleError(request, e.getMessage(), Status.INTERNAL_SERVER_ERROR);
255 public Response processGet(String content, HttpServletRequest request, HttpHeaders headers,
256 HttpServletResponse httpResponse, String index, String id,
257 DocumentStoreInterface documentStore) {
259 // Initialize the MDC Context for logging purposes.
260 ApiUtils.initMdcContext(request, headers);
263 ObjectMapper mapper = new ObjectMapper();
264 mapper.setSerializationInclusion(Include.NON_EMPTY);
267 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.GET,
268 ApiUtils.SEARCH_AUTH_POLICY_NAME);
269 } catch (Exception e) {
270 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL,
271 "DocumentApi.processGet",
273 return handleError(request, content, Status.FORBIDDEN);
277 return handleError(request, content, Status.FORBIDDEN);
280 String resourceVersion = headers.getRequestHeaders()
281 .getFirst(REQUEST_HEADER_RESOURCE_VERSION);
283 DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl();
285 document.setVersion(resourceVersion);
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());
292 output = result.getError() != null
293 ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
294 : result.getFailureCause();
296 if (httpResponse != null) {
297 httpResponse.setHeader(RESPONSE_HEADER_RESOURCE_VERSION, result.getResultVersion());
299 Response response = Response.status(result.getResultCode()).entity(output).build();
300 logResult(request, Response.Status.fromStatusCode(response.getStatus()));
302 // Clear the MDC context so that no other transaction inadvertently
303 // uses our transaction id.
304 ApiUtils.clearMdcContext();
307 } catch (Exception e) {
308 return handleError(request, e.getMessage(), Status.INTERNAL_SERVER_ERROR);
312 public Response processSearchWithGet(String content, HttpServletRequest request,
313 HttpHeaders headers, String index,
314 String queryText, DocumentStoreInterface documentStore) {
316 // Initialize the MDC Context for logging purposes.
317 ApiUtils.initMdcContext(request, headers);
320 ObjectMapper mapper = new ObjectMapper();
321 mapper.setSerializationInclusion(Include.NON_EMPTY);
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",
331 return handleError(request, content, Status.FORBIDDEN);
335 return handleError(request, content, Status.FORBIDDEN);
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());
344 output = result.getError() != null
345 ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
346 : result.getFailureCause();
348 Response response = Response.status(result.getResultCode()).entity(output).build();
350 // Clear the MDC context so that no other transaction inadvertently
351 // uses our transaction id.
352 ApiUtils.clearMdcContext();
355 } catch (Exception e) {
356 return handleError(request, e.getMessage(), Status.INTERNAL_SERVER_ERROR);
360 public Response queryWithGetWithPayload(String content, HttpServletRequest request,
361 HttpHeaders headers, String index,
362 DocumentStoreInterface documentStore) {
364 // Initialize the MDC Context for logging purposes.
365 ApiUtils.initMdcContext(request, headers);
367 logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "GET", (request != null)
368 ? request.getRequestURL().toString() : "");
369 if (logger.isDebugEnabled()) {
370 logger.debug("Request Body: " + content);
372 return processQuery(index, content, request, headers, documentStore);
375 public Response processSearchWithPost(String content, HttpServletRequest request,
376 HttpHeaders headers, String index,
377 DocumentStoreInterface documentStore) {
379 // Initialize the MDC Context for logging purposes.
380 ApiUtils.initMdcContext(request, headers);
382 logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "POST", (request != null)
383 ? request.getRequestURL().toString() : "");
384 if (logger.isDebugEnabled()) {
385 logger.debug("Request Body: " + content);
388 return processQuery(index, content, request, headers, documentStore);
392 * Common handler for query requests. This is called by both the GET with
393 * payload and POST with payload variants of the query endpoint.
395 * @param index - The index to be queried against.
396 * @param content - The payload containing the query structure.
397 * @param request - The HTTP request.
398 * @param headers - The HTTP headers.
399 * @return - A standard HTTP response.
401 private Response processQuery(String index, String content, HttpServletRequest request,
402 HttpHeaders headers, DocumentStoreInterface documentStore) {
405 ObjectMapper mapper = new ObjectMapper();
406 mapper.setSerializationInclusion(Include.NON_EMPTY);
408 // Make sure that we were supplied a payload before proceeding.
409 if (content == null) {
410 return handleError(request, content, Status.BAD_REQUEST);
413 // Validate that the request has the appropriate authorization.
416 isValid = searchService.validateRequest(headers, request, ApiUtils.Action.POST,
417 ApiUtils.SEARCH_AUTH_POLICY_NAME);
419 } catch (Exception e) {
420 logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL,
423 return handleError(request, content, Status.FORBIDDEN);
427 return handleError(request, content, Status.FORBIDDEN);
430 SearchStatement searchStatement;
433 // Marshall the supplied request payload into a search statement
435 searchStatement = mapper.readValue(content, SearchStatement.class);
437 } catch (Exception e) {
438 return handleError(request, e.getMessage(), Status.BAD_REQUEST);
441 // Now, submit the search statement, translated into
442 // ElasticSearch syntax, to the document store DAO.
443 SearchOperationResult result = documentStore.searchWithPayload(index,
444 searchStatement.toElasticSearch());
445 String output = null;
446 if (result.getResultCode() >= 200 && result.getResultCode() <= 299) {
447 output = prepareOutput(mapper, result);
449 output = result.getError() != null
450 ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError())
451 : result.getFailureCause();
453 Response response = Response.status(result.getResultCode()).entity(output).build();
455 // Clear the MDC context so that no other transaction inadvertently
456 // uses our transaction id.
457 ApiUtils.clearMdcContext();
461 } catch (Exception e) {
462 return handleError(request, e.getMessage(), Status.INTERNAL_SERVER_ERROR);
466 private String prepareOutput(ObjectMapper mapper, SearchOperationResult result)
467 throws JsonProcessingException {
468 StringBuffer output = new StringBuffer();
469 output.append("{\r\n\"searchResult\":");
470 output.append(mapper.writerWithDefaultPrettyPrinter()
471 .writeValueAsString(result.getSearchResult()));
472 AggregationResults aggs = result.getAggregationResult();
474 output.append(",\r\n\"aggregationResult\":");
475 output.append(mapper.setSerializationInclusion(Include.NON_NULL)
476 .writerWithDefaultPrettyPrinter().writeValueAsString(aggs));
478 output.append("\r\n}");
479 return output.toString();
482 private Response handleError(HttpServletRequest request, String message, Status status) {
483 logResult(request, status);
484 return Response.status(status).entity(message).type(MediaType.APPLICATION_JSON).build();
487 void logResult(HttpServletRequest request, Response.Status status) {
489 logger.info(SearchDbMsgs.PROCESS_REST_REQUEST, (request != null) ? request.getMethod() : "",
490 (request != null) ? request.getRequestURL().toString() : "",
491 (request != null) ? request.getRemoteHost() : "", Integer.toString(status.getStatusCode()));
493 auditLogger.info(SearchDbMsgs.PROCESS_REST_REQUEST,
495 .setField(LogLine.DefinedFields.RESPONSE_CODE, status.getStatusCode())
496 .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, status.getReasonPhrase()),
497 (request != null) ? request.getMethod() : "",
498 (request != null) ? request.getRequestURL().toString() : "",
499 (request != null) ? request.getRemoteHost() : "", Integer.toString(status.getStatusCode()));
501 // Clear the MDC context so that no other transaction inadvertently
502 // uses our transaction id.
503 ApiUtils.clearMdcContext();