2 * ============LICENSE_START=======================================================
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
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
21 package org.onap.aai.sa.rest;
23 import com.fasterxml.jackson.databind.ObjectMapper;
25 import org.onap.aai.sa.searchdbabstraction.elasticsearch.dao.DocumentStoreInterface;
26 import org.onap.aai.sa.searchdbabstraction.elasticsearch.exception.DocumentStoreOperationException;
27 import org.onap.aai.sa.searchdbabstraction.entity.OperationResult;
28 import org.onap.aai.sa.searchdbabstraction.logging.SearchDbMsgs;
29 import org.onap.aai.cl.api.LogFields;
30 import org.onap.aai.cl.api.LogLine;
31 import org.onap.aai.cl.api.Logger;
32 import org.onap.aai.cl.eelf.LoggerFactory;
33 import org.onap.aai.sa.rest.DocumentFieldSchema;
34 import org.onap.aai.sa.rest.DocumentSchema;
36 import java.io.FileNotFoundException;
37 import java.io.IOException;
38 import javax.servlet.http.HttpServletRequest;
39 import javax.ws.rs.core.HttpHeaders;
40 import javax.ws.rs.core.Response;
44 * This class encapsulates the REST end points associated with manipulating
45 * indexes in the document store.
47 public class IndexApi {
49 protected SearchServiceApi searchService = null;
52 * Configuration for the custom analyzers that will be used for indexing.
54 protected AnalysisConfiguration analysisConfig;
56 // Set up the loggers.
57 private static Logger logger = LoggerFactory.getInstance()
58 .getLogger(IndexApi.class.getName());
59 private static Logger auditLogger = LoggerFactory.getInstance()
60 .getAuditLogger(IndexApi.class.getName());
63 public IndexApi(SearchServiceApi searchService) {
64 this.searchService = searchService;
70 * Initializes the end point.
72 * @throws FileNotFoundException
74 * @throws DocumentStoreOperationException
78 // Instantiate our analysis configuration object.
79 analysisConfig = new AnalysisConfiguration();
84 * Processes client requests to create a new index and document type in the
87 * @param documentSchema - The contents of the request body which is expected
88 * to be a JSON structure which corresponds to the
89 * schema defined in document.schema.json
90 * @param index - The name of the index to create.
91 * @return - A Standard REST response
93 public Response processCreateIndex(String documentSchema,
94 HttpServletRequest request,
97 DocumentStoreInterface documentStore) {
100 String resultString = "Unexpected error";
102 // Initialize the MDC Context for logging purposes.
103 ApiUtils.initMdcContext(request, headers);
105 // Validate that the request is correctly authenticated before going
109 if (!searchService.validateRequest(headers, request,
110 ApiUtils.Action.POST, ApiUtils.SEARCH_AUTH_POLICY_NAME)) {
111 logger.warn(SearchDbMsgs.INDEX_CREATE_FAILURE, index, "Authentication failure.");
112 return errorResponse(Response.Status.FORBIDDEN, "Authentication failure.", request);
115 } catch (Exception e) {
117 logger.warn(SearchDbMsgs.INDEX_CREATE_FAILURE, index,
118 "Unexpected authentication failure - cause: " + e.getMessage());
119 return errorResponse(Response.Status.FORBIDDEN, "Authentication failure.", request);
123 // We expect a payload containing the document schema. Make sure
125 if (documentSchema == null) {
126 logger.warn(SearchDbMsgs.INDEX_CREATE_FAILURE, index, "Missing document schema payload");
127 return errorResponse(Response.Status.fromStatusCode(resultCode), "Missing payload", request);
132 // Marshal the supplied json string into a document schema object.
133 ObjectMapper mapper = new ObjectMapper();
134 DocumentSchema schema = mapper.readValue(documentSchema, DocumentSchema.class);
136 // Now, ask the DAO to create the index.
137 OperationResult result = documentStore.createIndex(index, schema);
139 // Extract the result code and string from the OperationResult
140 // object so that we can use them to generate a standard REST
142 // Note that we want to return a 201 result code on a successful
143 // create, so if we get back a 200 from the document store,
144 // translate that int a 201.
145 resultCode = (result.getResultCode() == 200) ? 201 : result.getResultCode();
146 resultString = (result.getFailureCause() == null)
147 ? result.getResult() : result.getFailureCause();
149 } catch (com.fasterxml.jackson.core.JsonParseException
150 | com.fasterxml.jackson.databind.JsonMappingException e) {
152 // We were unable to marshal the supplied json string into a valid
153 // document schema, so return an appropriate error response.
154 resultCode = javax.ws.rs.core.Response.Status.BAD_REQUEST.getStatusCode();
155 resultString = "Malformed schema: " + e.getMessage();
157 } catch (IOException e) {
159 // We'll treat this is a general internal error.
160 resultCode = javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR.getStatusCode();
161 resultString = "IO Failure: " + e.getMessage();
164 Response response = Response.status(resultCode).entity(resultString).build();
167 if ((response.getStatus() >= 200) && (response.getStatus() < 300)) {
168 logger.info(SearchDbMsgs.CREATED_INDEX, index);
170 logger.warn(SearchDbMsgs.INDEX_CREATE_FAILURE, index, resultString);
173 // Generate our audit log.
174 auditLogger.info(SearchDbMsgs.PROCESS_REST_REQUEST,
176 .setField(LogLine.DefinedFields.RESPONSE_CODE, resultCode)
177 .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
178 Response.Status.fromStatusCode(resultCode).toString()),
179 (request != null) ? request.getMethod() : "Unknown",
180 (request != null) ? request.getRequestURL().toString() : "Unknown",
181 (request != null) ? request.getRemoteHost() : "Unknown",
182 Integer.toString(response.getStatus()));
184 // Clear the MDC context so that no other transaction inadvertently
185 // uses our transaction id.
186 ApiUtils.clearMdcContext();
188 // Finally, return the response.
194 * Processes a client request to remove an index from the document store.
195 * Note that this implicitly deletes all documents contained within that index.
197 * @param index - The index to be deleted.
198 * @return - A standard REST response.
200 public Response processDelete(String index,
201 HttpServletRequest request,
203 DocumentStoreInterface documentStore) {
205 // Initialize the MDC Context for logging purposes.
206 ApiUtils.initMdcContext(request, headers);
208 // Set a default response in case something unexpected goes wrong.
209 Response response = Response.status(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR)
213 // Validate that the request is correctly authenticated before going
217 if (!searchService.validateRequest(headers, request, ApiUtils.Action.POST,
218 ApiUtils.SEARCH_AUTH_POLICY_NAME)) {
219 logger.warn(SearchDbMsgs.INDEX_CREATE_FAILURE, index, "Authentication failure.");
220 return errorResponse(Response.Status.FORBIDDEN, "Authentication failure.", request);
223 } catch (Exception e) {
225 logger.warn(SearchDbMsgs.INDEX_CREATE_FAILURE, index,
226 "Unexpected authentication failure - cause: " + e.getMessage());
227 return errorResponse(Response.Status.FORBIDDEN, "Authentication failure.", request);
232 // Send the request to the document store.
233 response = responseFromOperationResult(documentStore.deleteIndex(index));
235 } catch (DocumentStoreOperationException e) {
236 response = Response.status(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR)
237 .entity(e.getMessage())
243 if ((response.getStatus() >= 200) && (response.getStatus() < 300)) {
244 logger.info(SearchDbMsgs.DELETED_INDEX, index);
246 logger.warn(SearchDbMsgs.INDEX_DELETE_FAILURE, index, (String) response.getEntity());
249 // Generate our audit log.
250 auditLogger.info(SearchDbMsgs.PROCESS_REST_REQUEST,
252 .setField(LogLine.DefinedFields.RESPONSE_CODE, response.getStatus())
253 .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
254 response.getStatusInfo().getReasonPhrase()),
255 (request != null) ? request.getMethod() : "Unknown",
256 (request != null) ? request.getRequestURL().toString() : "Unknown",
257 (request != null) ? request.getRemoteHost() : "Unknown",
258 Integer.toString(response.getStatus()));
260 // Clear the MDC context so that no other transaction inadvertently
261 // uses our transaction id.
262 ApiUtils.clearMdcContext();
269 * This method takes a JSON format document schema and produces a set of
270 * field mappings in the form that Elastic Search expects.
272 * @param documentSchema - A document schema expressed as a JSON string.
273 * @return - A JSON string expressing an Elastic Search mapping configuration.
274 * @throws com.fasterxml.jackson.core.JsonParseException
275 * @throws com.fasterxml.jackson.databind.JsonMappingException
276 * @throws IOException
278 public String generateDocumentMappings(String documentSchema)
279 throws com.fasterxml.jackson.core.JsonParseException,
280 com.fasterxml.jackson.databind.JsonMappingException, IOException {
282 // Unmarshal the json content into a document schema object.
283 ObjectMapper mapper = new ObjectMapper();
284 DocumentSchema schema = mapper.readValue(documentSchema, DocumentSchema.class);
286 // Now, generate the Elastic Search mapping json and return it.
287 StringBuilder sb = new StringBuilder();
289 sb.append("\"properties\": {");
291 boolean first = true;
292 for (DocumentFieldSchema field : schema.getFields()) {
300 sb.append("\"").append(field.getName()).append("\": {");
302 // The field type is mandatory.
303 sb.append("\"type\": \"").append(field.getDataType()).append("\"");
305 // If the index field was specified, then append it.
306 if (field.getSearchable() != null) {
307 sb.append(", \"index\": \"").append(field.getSearchable()
308 ? "analyzed" : "not_analyzed").append("\"");
311 // If a search analyzer was specified, then append it.
312 if (field.getSearchAnalyzer() != null) {
313 sb.append(", \"search_analyzer\": \"").append(field.getSearchAnalyzer()).append("\"");
316 // If an indexing analyzer was specified, then append it.
317 if (field.getIndexAnalyzer() != null) {
318 sb.append(", \"analyzer\": \"").append(field.getIndexAnalyzer()).append("\"");
320 sb.append(", \"analyzer\": \"").append("whitespace").append("\"");
329 logger.debug("Generated document mappings: " + sb.toString());
331 return sb.toString();
336 * Converts an {@link OperationResult} to a standard REST {@link Response}
339 * @param result - The {@link OperationResult} to be converted.
340 * @return - The equivalent {@link Response} object.
342 public Response responseFromOperationResult(OperationResult result) {
344 if ((result.getResultCode() >= 200) && (result.getResultCode() < 300)) {
345 return Response.status(result.getResultCode()).entity(result.getResult()).build();
347 if (result.getFailureCause() != null) {
348 return Response.status(result.getResultCode()).entity(result.getFailureCause()).build();
350 return Response.status(result.getResultCode()).entity(result.getResult()).build();
355 public Response errorResponse(Response.Status status, String msg, HttpServletRequest request) {
357 // Generate our audit log.
358 auditLogger.info(SearchDbMsgs.PROCESS_REST_REQUEST,
360 .setField(LogLine.DefinedFields.RESPONSE_CODE, status.getStatusCode())
361 .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, status.getReasonPhrase()),
362 (request != null) ? request.getMethod() : "Unknown",
363 (request != null) ? request.getRequestURL().toString() : "Unknown",
364 (request != null) ? request.getRemoteHost() : "Unknown",
365 Integer.toString(status.getStatusCode()));
367 // Clear the MDC context so that no other transaction inadvertently
368 // uses our transaction id.
369 ApiUtils.clearMdcContext();
371 return Response.status(status)