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.sparky.search;
23 import java.util.Arrays;
24 import java.util.HashMap;
25 import java.util.List;
27 import java.util.Map.Entry;
28 import java.util.TreeMap;
30 import org.apache.camel.Exchange;
31 import org.apache.camel.Processor;
32 import org.apache.camel.component.restlet.RestletConstants;
33 import org.json.JSONArray;
34 import org.json.JSONObject;
35 import org.onap.aai.cl.api.Logger;
36 import org.onap.aai.cl.eelf.LoggerFactory;
37 import org.onap.aai.cl.mdc.MdcContext;
38 import org.onap.aai.restclient.client.OperationResult;
39 import org.onap.aai.sparky.dal.ElasticSearchAdapter;
40 import org.onap.aai.sparky.inventory.EntityHistoryQueryBuilder;
41 import org.onap.aai.sparky.logging.AaiUiMsgs;
42 import org.onap.aai.sparky.util.NodeUtils;
43 import org.onap.aai.sparky.util.RestletUtils;
44 import org.restlet.Request;
45 import org.restlet.Response;
46 import org.restlet.data.ClientInfo;
47 import org.restlet.data.MediaType;
48 import org.restlet.data.Status;
50 import com.fasterxml.jackson.core.JsonProcessingException;
51 import com.fasterxml.jackson.databind.JsonNode;
52 import com.fasterxml.jackson.databind.ObjectMapper;
53 import com.fasterxml.jackson.databind.SerializationFeature;
56 * Receives and processes Entity Count History requests
58 public class EntityCountHistoryProcessor implements Processor {
60 private static final Logger LOG =
61 LoggerFactory.getInstance().getLogger(EntityCountHistoryProcessor.class);
63 private static final long serialVersionUID = 1L;
65 private ElasticSearchAdapter elasticSearchAdapter = null;
66 private ObjectMapper mapper;
68 private static final String SEARCH_PRETTY_STRING = "_search?pretty";
69 private static final String TYPE = "type";
70 private static final String TABLE = "table";
71 private static final String GRAPH = "graph";
73 private List<String> entityTypesToSummarize;
74 private List<String> vnfEntityTypes;
76 private String entityCountHistoryIndexName;
78 private boolean summarizeVnfs = false;
80 private RestletUtils restletUtils = new RestletUtils();
83 * Instantiates a new Entity Count History
86 public EntityCountHistoryProcessor(ElasticSearchAdapter elasticSearchAdapter,
87 String entityTypesToSummarizeDelimitedList, String vnfEntityTypesDelimitedList, String entityCountHistoryIndexName) {
89 this.elasticSearchAdapter = elasticSearchAdapter;
90 this.entityCountHistoryIndexName = entityCountHistoryIndexName;
92 entityTypesToSummarize =
93 Arrays.asList(entityTypesToSummarizeDelimitedList.toLowerCase().split("[\\s,]+"));
96 Arrays.asList(vnfEntityTypesDelimitedList.toLowerCase().split("[\\s,]+"));
98 summarizeVnfs = vnfEntityTypesDelimitedList.toLowerCase().contains("vnf");
100 this.mapper = new ObjectMapper();
101 this.mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
105 * Processes a entity count history search request
107 * @param exchange The Exchange object generated by Apache Camel for the incoming request
111 public void process(Exchange exchange) throws Exception {
113 Request request = exchange.getIn().getHeader(RestletConstants.RESTLET_REQUEST, Request.class);
114 Response restletResponse =
115 exchange.getIn().getHeader(RestletConstants.RESTLET_RESPONSE, Response.class);
117 Object xTransactionId = exchange.getIn().getHeader("X-TransactionId");
118 if (xTransactionId == null) {
119 xTransactionId = NodeUtils.getRandomTxnId();
122 Object partnerName = exchange.getIn().getHeader("X-FromAppId");
123 if (partnerName == null) {
124 partnerName = "Browser";
128 * Disables automatic Apache Camel Restlet component logging which prints out an undesirable log
129 * entry which includes client (e.g. browser) information
131 request.setLoggable(false);
133 ClientInfo clientInfo = request.getClientInfo();
134 MdcContext.initialize((String) xTransactionId, "AAI-UI", "", (String) partnerName,
135 clientInfo.getAddress() + ":" + clientInfo.getPort());
137 String typeParameter = getTypeParameter(exchange);
139 if (null != typeParameter && !typeParameter.isEmpty()) {
140 OperationResult operationResult = null;
143 operationResult = getResults(restletResponse, typeParameter);
144 restletResponse.setEntity(operationResult.getResult(), MediaType.APPLICATION_JSON);
145 } catch (Exception exc) {
146 LOG.error(AaiUiMsgs.CONFIGURATION_ERROR, exc.getLocalizedMessage());
149 LOG.error(AaiUiMsgs.RESOURCE_NOT_FOUND, request.getOriginalRef().toString());
150 String errorMessage =
151 restletUtils.generateJsonErrorResponse("Unsupported request. Resource not found.");
152 restletResponse.setEntity(errorMessage, MediaType.APPLICATION_JSON);
153 restletResponse.setStatus(Status.CLIENT_ERROR_NOT_FOUND);
156 exchange.getOut().setBody(restletResponse);
161 * Format line graph output
163 * @param results The results
164 * @return The JSON object
165 * @throws JsonProcessingException The JSON processing exception
167 public JSONObject formatLineGraphOutput(String results) throws JsonProcessingException {
168 Map<Long, Long> countByDateMap = new HashMap<Long, Long>();
170 JsonNode resultNode = null;
172 JSONObject finalResult = new JSONObject();
173 JSONArray finalResultArr = new JSONArray();
176 resultNode = mapper.readTree(results);
178 final JsonNode bucketsNode = getBucketsNode(resultNode);
180 if (bucketsNode.isArray()) {
182 for (final JsonNode entityNode : bucketsNode) {
183 final JsonNode dateBucketNode = entityNode.get("group_by_date").get("buckets");
184 if (dateBucketNode.isArray()) {
185 for (final JsonNode dateBucket : dateBucketNode) {
186 Long date = dateBucket.get("key").asLong();
187 final JsonNode countBucketNode =
188 dateBucket.get("sort_by_date").get("hits").get("hits");
190 if (countBucketNode.isArray()) {
191 final JsonNode latestEntityNode = countBucketNode.get(0);
193 long currentCount = latestEntityNode.get("_source").get("count").asLong();
194 if (countByDateMap.containsKey(date)) {
195 // add to the value if map already contains this date
196 currentCount += countByDateMap.get(date);
199 countByDateMap.put(date, currentCount);
208 * Sort the map by epoch timestamp
210 Map<Long, Long> sortedMap = new TreeMap<Long, Long>(countByDateMap);
211 for (Entry<Long, Long> entry : sortedMap.entrySet()) {
212 JSONObject dateEntry = new JSONObject();
213 dateEntry.put("date", entry.getKey());
214 dateEntry.put("count", entry.getValue());
215 finalResultArr.put(dateEntry);
218 } catch (Exception exc) {
219 LOG.warn(AaiUiMsgs.ERROR_BUILDING_SEARCH_RESPONSE, exc.getLocalizedMessage());
222 return finalResult.put("result", finalResultArr);
226 * Format table output
228 * @param results The results
229 * @return The JSON object
230 * @throws JsonProcessingException The JSON processing exception
232 public JSONObject formatTableOutput(String results) throws JsonProcessingException {
233 JsonNode resultNode = null;
235 JSONObject finalResult = new JSONObject();
236 JSONArray entitiesArr = new JSONArray();
238 Map<String, Long> entityCountInTable = initializeEntityMap();
243 resultNode = mapper.readTree(results);
245 final JsonNode bucketsNode = getBucketsNode(resultNode);
246 if (bucketsNode.isArray()) {
248 for (final JsonNode entityNode : bucketsNode) {
249 String entityType = entityNode.get("key").asText();
250 boolean isAVnf = vnfEntityTypes.contains(entityType);
253 if (isAVnf || entityCountInTable.get(entityType) != null) {
254 final JsonNode hitsBucketNode = entityNode.get("sort_by_date").get("hits").get("hits");
255 if (hitsBucketNode.isArray()) {
256 // the first bucket will be the latest
257 final JsonNode hitNode = hitsBucketNode.get(0);
259 countValue = hitNode.get("_source").get("count").asLong();
262 * Special case: Add all the VNF types together to get aggregate count
264 if (summarizeVnfs && isAVnf) {
265 vnfCount += countValue;
266 countValue = vnfCount;
270 entityCountInTable.replace(entityType, countValue);
276 for (Entry<String, Long> entry : entityCountInTable.entrySet()) {
277 JSONObject entityType = new JSONObject();
278 entityType.put("key", entry.getKey());
279 entityType.put("doc_count", entry.getValue());
280 entitiesArr.put(entityType);
283 finalResult.put("result", entitiesArr);
285 } catch (Exception exc) {
286 LOG.warn(AaiUiMsgs.ERROR_BUILDING_RESPONSE_FOR_TABLE_QUERY, exc.getLocalizedMessage());
295 * @param response The response
296 * @param type The type
297 * @return The results
299 public OperationResult getResults(Response response, String type) {
300 OperationResult operationResult = new OperationResult();
302 String reqPayload = EntityHistoryQueryBuilder.getQuery(type).toString();
305 final String fullUrlStr = elasticSearchAdapter
306 .buildElasticSearchUrlForApi(entityCountHistoryIndexName, SEARCH_PRETTY_STRING);
308 OperationResult opResult = elasticSearchAdapter.doPost(fullUrlStr, reqPayload,
309 javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE);
311 JSONObject finalOutput = null;
312 if (type.equalsIgnoreCase(TABLE)) {
313 finalOutput = formatTableOutput(opResult.getResult());
314 } else if (type.equalsIgnoreCase(GRAPH)) {
315 finalOutput = formatLineGraphOutput(opResult.getResult());
318 if (finalOutput != null) {
319 response.setEntity(finalOutput.toString(), MediaType.APPLICATION_JSON);
320 operationResult.setResult(finalOutput.toString());
322 } catch (JsonProcessingException exc) {
323 restletUtils.handleRestletErrors(LOG, "Unable to map JSONpayload", exc, response);
326 return operationResult;
330 * Gets the buckets node
332 * @param node The node
333 * @return The buckets node
334 * @throws Exception The exception
336 public JsonNode getBucketsNode(JsonNode node) throws Exception {
337 if (node.get("aggregations").get("group_by_entityType").get("buckets") != null) {
338 return node.get("aggregations").get("group_by_entityType").get("buckets");
340 throw new Exception("Failed to map JSON response");
345 * Initialize entity map
349 private Map<String, Long> initializeEntityMap() {
350 Map<String, Long> entityMap = new HashMap<String, Long>();
351 for (String entity : entityTypesToSummarize) {
352 entityMap.put(entity, (long) 0);
359 * Extracts the "type" query parameter from the request URI
362 * @return String containing the value of the "type" query parameter of the request. Returns null
363 * if no "type" parameter found
365 public String getTypeParameter(Exchange exchange) {
366 String typeParameter = null;
368 String requestUriParameterString = exchange.getIn().getHeader("CamelHttpQuery", String.class);
370 if (null != requestUriParameterString) {
371 String[] requestParameterParts = requestUriParameterString.split("&");
373 String[] parameter = requestParameterParts[0].split("=");
374 String currentParameterKey = parameter[0];
376 if (null != currentParameterKey && !currentParameterKey.isEmpty()) {
377 // Check if we're looking at the "type" parameter key
378 if (currentParameterKey.equals(TYPE)) {
379 boolean uriIncludesTypeParameterValue =
380 (parameter.length >= 2) && !parameter[1].isEmpty();
382 if (uriIncludesTypeParameterValue) {
383 String typeParameterValue = parameter[1];
385 // Is the parameter value one that we return data for?
386 if (typeParameterValue.equalsIgnoreCase(TABLE)
387 || typeParameterValue.equalsIgnoreCase(GRAPH)) {
388 typeParameter = typeParameterValue;
395 return typeParameter;
399 public void setRestletUtils(RestletUtils restletUtils) {
400 this.restletUtils = restletUtils;