changed the header license to new license
[aai/sparky-be.git] / src / main / java / org / onap / aai / sparky / search / EntityCountHistoryProcessor.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.sparky.search;
22
23 import java.util.Arrays;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.TreeMap;
29
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;
49
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;
54
55 /**
56  * Receives and processes Entity Count History requests
57  */
58 public class EntityCountHistoryProcessor implements Processor {
59
60   private static final Logger LOG =
61       LoggerFactory.getInstance().getLogger(EntityCountHistoryProcessor.class);
62
63   private static final long serialVersionUID = 1L;
64
65   private ElasticSearchAdapter elasticSearchAdapter = null;
66   private ObjectMapper mapper;
67   
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";
72
73   private List<String> entityTypesToSummarize;
74   private List<String> vnfEntityTypes;
75   
76   private String entityCountHistoryIndexName;
77
78   private boolean summarizeVnfs = false;
79
80   private RestletUtils restletUtils = new RestletUtils();
81
82   /**
83    * Instantiates a new Entity Count History
84    */
85   
86   public EntityCountHistoryProcessor(ElasticSearchAdapter elasticSearchAdapter,
87       String entityTypesToSummarizeDelimitedList, String vnfEntityTypesDelimitedList, String entityCountHistoryIndexName) {
88
89     this.elasticSearchAdapter = elasticSearchAdapter;
90     this.entityCountHistoryIndexName = entityCountHistoryIndexName;
91
92     entityTypesToSummarize =
93         Arrays.asList(entityTypesToSummarizeDelimitedList.toLowerCase().split("[\\s,]+"));
94
95     vnfEntityTypes =
96         Arrays.asList(vnfEntityTypesDelimitedList.toLowerCase().split("[\\s,]+"));
97     
98     summarizeVnfs = vnfEntityTypesDelimitedList.toLowerCase().contains("vnf");
99
100     this.mapper = new ObjectMapper();
101     this.mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
102   }
103   
104   /**
105    * Processes a entity count history search request
106    *
107    * @param exchange The Exchange object generated by Apache Camel for the incoming request
108    */
109
110   @Override
111   public void process(Exchange exchange) throws Exception {
112           
113           Request request = exchange.getIn().getHeader(RestletConstants.RESTLET_REQUEST, Request.class);
114             Response restletResponse =
115                 exchange.getIn().getHeader(RestletConstants.RESTLET_RESPONSE, Response.class);
116
117             Object xTransactionId = exchange.getIn().getHeader("X-TransactionId");
118             if (xTransactionId == null) {
119               xTransactionId = NodeUtils.getRandomTxnId();
120             }
121
122             Object partnerName = exchange.getIn().getHeader("X-FromAppId");
123             if (partnerName == null) {
124               partnerName = "Browser";
125             }
126
127             /*
128              * Disables automatic Apache Camel Restlet component logging which prints out an undesirable log
129              * entry which includes client (e.g. browser) information
130              */
131             request.setLoggable(false);
132
133             ClientInfo clientInfo = request.getClientInfo();
134             MdcContext.initialize((String) xTransactionId, "AAI-UI", "", (String) partnerName,
135                 clientInfo.getAddress() + ":" + clientInfo.getPort());
136
137             String typeParameter = getTypeParameter(exchange);
138
139             if (null != typeParameter && !typeParameter.isEmpty()) {
140               OperationResult operationResult = null;
141
142               try {
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());
147               }
148             } else {
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);
154             }
155
156             exchange.getOut().setBody(restletResponse);
157   }
158
159
160   /**
161    * Format line graph output
162    *
163    * @param results The results
164    * @return The JSON object
165    * @throws JsonProcessingException The JSON processing exception
166    */
167   public JSONObject formatLineGraphOutput(String results) throws JsonProcessingException {
168     Map<Long, Long> countByDateMap = new HashMap<Long, Long>();
169
170     JsonNode resultNode = null;
171
172     JSONObject finalResult = new JSONObject();
173     JSONArray finalResultArr = new JSONArray();
174
175     try {
176       resultNode = mapper.readTree(results);
177
178       final JsonNode bucketsNode = getBucketsNode(resultNode);
179
180       if (bucketsNode.isArray()) {
181
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");
189
190               if (countBucketNode.isArray()) {
191                 final JsonNode latestEntityNode = countBucketNode.get(0);
192
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);
197                 }
198
199                 countByDateMap.put(date, currentCount);
200               }
201             }
202
203           }
204         }
205       }
206
207       /*
208        * Sort the map by epoch timestamp
209        */
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);
216       }
217
218     } catch (Exception exc) {
219       LOG.warn(AaiUiMsgs.ERROR_BUILDING_SEARCH_RESPONSE, exc.getLocalizedMessage());
220     }
221
222     return finalResult.put("result", finalResultArr);
223   }
224
225   /**
226    * Format table output
227    *
228    * @param results The results
229    * @return The JSON object
230    * @throws JsonProcessingException The JSON processing exception
231    */
232   public JSONObject formatTableOutput(String results) throws JsonProcessingException {
233     JsonNode resultNode = null;
234
235     JSONObject finalResult = new JSONObject();
236     JSONArray entitiesArr = new JSONArray();
237
238     Map<String, Long> entityCountInTable = initializeEntityMap();
239
240     long vnfCount = 0;
241
242     try {
243       resultNode = mapper.readTree(results);
244
245       final JsonNode bucketsNode = getBucketsNode(resultNode);
246       if (bucketsNode.isArray()) {
247
248         for (final JsonNode entityNode : bucketsNode) {
249           String entityType = entityNode.get("key").asText();
250           boolean isAVnf = vnfEntityTypes.contains(entityType);
251           long countValue = 0;
252
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);
258
259               countValue = hitNode.get("_source").get("count").asLong();
260
261               /*
262                * Special case: Add all the VNF types together to get aggregate count
263                */
264               if (summarizeVnfs && isAVnf) {
265                 vnfCount += countValue;
266                 countValue = vnfCount;
267                 entityType = "vnf";
268               }
269
270               entityCountInTable.replace(entityType, countValue);
271             }
272           }
273
274         }
275       }
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);
281       }
282
283       finalResult.put("result", entitiesArr);
284
285     } catch (Exception exc) {
286       LOG.warn(AaiUiMsgs.ERROR_BUILDING_RESPONSE_FOR_TABLE_QUERY, exc.getLocalizedMessage());
287     }
288
289     return finalResult;
290   }
291
292   /**
293    * Gets the results
294    *
295    * @param response The response
296    * @param type The type
297    * @return The results
298    */
299   public OperationResult getResults(Response response, String type) {
300     OperationResult operationResult = new OperationResult();
301
302     String reqPayload = EntityHistoryQueryBuilder.getQuery(type).toString();
303
304     try {
305       final String fullUrlStr = elasticSearchAdapter
306           .buildElasticSearchUrlForApi(entityCountHistoryIndexName, SEARCH_PRETTY_STRING);
307
308       OperationResult opResult = elasticSearchAdapter.doPost(fullUrlStr, reqPayload,
309           javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE);
310
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());
316       }
317
318       if (finalOutput != null) {
319         response.setEntity(finalOutput.toString(), MediaType.APPLICATION_JSON);
320         operationResult.setResult(finalOutput.toString());
321       }
322     } catch (JsonProcessingException exc) {
323       restletUtils.handleRestletErrors(LOG, "Unable to map JSONpayload", exc, response);
324     }
325
326     return operationResult;
327   }
328
329   /**
330    * Gets the buckets node
331    *
332    * @param node The node
333    * @return The buckets node
334    * @throws Exception The exception
335    */
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");
339     } else {
340       throw new Exception("Failed to map JSON response");
341     }
342   }
343
344   /**
345    * Initialize entity map
346    *
347    * @return the map
348    */
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);
353     }
354
355     return entityMap;
356   }
357
358   /**
359    * Extracts the "type" query parameter from the request URI
360    *
361    * @param exchange
362    * @return String containing the value of the "type" query parameter of the request. Returns null
363    *         if no "type" parameter found
364    */
365   public String getTypeParameter(Exchange exchange) {
366     String typeParameter = null;
367
368     String requestUriParameterString = exchange.getIn().getHeader("CamelHttpQuery", String.class);
369
370     if (null != requestUriParameterString) {
371       String[] requestParameterParts = requestUriParameterString.split("&");
372
373       String[] parameter = requestParameterParts[0].split("=");
374       String currentParameterKey = parameter[0];
375
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();
381
382           if (uriIncludesTypeParameterValue) {
383             String typeParameterValue = parameter[1];
384
385             // Is the parameter value one that we return data for?
386             if (typeParameterValue.equalsIgnoreCase(TABLE)
387                 || typeParameterValue.equalsIgnoreCase(GRAPH)) {
388               typeParameter = typeParameterValue;
389             }
390           }
391         }
392       }
393     }
394
395     return typeParameter;
396   }
397
398
399   public void setRestletUtils(RestletUtils restletUtils) {
400     this.restletUtils = restletUtils;
401   }
402
403 }