Convert Sparky to Spring-Boot
[aai/sparky-be.git] / sparkybe-onap-service / src / main / java / org / onap / aai / sparky / search / EntityCountHistoryProcessor.java
1 /**
2  * ============LICENSE_START===================================================
3  * SPARKY (AAI UI service)
4  * ============================================================================
5  * Copyright © 2017 AT&T Intellectual Property.
6  * Copyright © 2017 Amdocs
7  * All rights reserved.
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 at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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=====================================================
21  *
22  * ECOMP and OpenECOMP are trademarks
23  * and service marks of AT&T Intellectual Property.
24  */
25 package org.onap.aai.sparky.search;
26
27 import java.util.Arrays;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Map.Entry;
32 import java.util.TreeMap;
33
34 import org.apache.camel.Exchange;
35 import org.apache.camel.Processor;
36 import org.apache.camel.component.restlet.RestletConstants;
37 import org.json.JSONArray;
38 import org.json.JSONObject;
39 import org.onap.aai.cl.api.Logger;
40 import org.onap.aai.cl.eelf.LoggerFactory;
41 import org.onap.aai.cl.mdc.MdcContext;
42 import org.onap.aai.restclient.client.OperationResult;
43 import org.onap.aai.sparky.dal.ElasticSearchAdapter;
44 import org.onap.aai.sparky.inventory.EntityHistoryQueryBuilder;
45 import org.onap.aai.sparky.logging.AaiUiMsgs;
46 import org.onap.aai.sparky.util.NodeUtils;
47 import org.onap.aai.sparky.util.RestletUtils;
48 import org.restlet.Request;
49 import org.restlet.Response;
50 import org.restlet.data.ClientInfo;
51 import org.restlet.data.MediaType;
52 import org.restlet.data.Status;
53
54 import com.fasterxml.jackson.core.JsonProcessingException;
55 import com.fasterxml.jackson.databind.JsonNode;
56 import com.fasterxml.jackson.databind.ObjectMapper;
57 import com.fasterxml.jackson.databind.SerializationFeature;
58
59 /**
60  * Receives and processes Entity Count History requests
61  */
62 public class EntityCountHistoryProcessor implements Processor {
63
64   private static final Logger LOG =
65       LoggerFactory.getInstance().getLogger(EntityCountHistoryProcessor.class);
66
67   private static final long serialVersionUID = 1L;
68
69   private ElasticSearchAdapter elasticSearchAdapter = null;
70   private ObjectMapper mapper;
71   
72   private static final String SEARCH_PRETTY_STRING = "_search?pretty";
73   private static final String TYPE = "type";
74   private static final String TABLE = "table";
75   private static final String GRAPH = "graph";
76
77   private List<String> entityTypesToSummarize;
78   private List<String> vnfEntityTypes;
79   
80   private String entityCountHistoryIndexName;
81
82   private boolean summarizeVnfs = false;
83
84   private RestletUtils restletUtils = new RestletUtils();
85
86   /**
87    * Instantiates a new Entity Count History
88    */
89   
90   public EntityCountHistoryProcessor(ElasticSearchAdapter elasticSearchAdapter,
91       String entityTypesToSummarizeDelimitedList, String vnfEntityTypesDelimitedList, String entityCountHistoryIndexName) {
92
93     this.elasticSearchAdapter = elasticSearchAdapter;
94     this.entityCountHistoryIndexName = entityCountHistoryIndexName;
95
96     entityTypesToSummarize =
97         Arrays.asList(entityTypesToSummarizeDelimitedList.toLowerCase().split("[\\s,]+"));
98
99     vnfEntityTypes =
100         Arrays.asList(vnfEntityTypesDelimitedList.toLowerCase().split("[\\s,]+"));
101     
102     summarizeVnfs = vnfEntityTypesDelimitedList.toLowerCase().contains("vnf");
103
104     this.mapper = new ObjectMapper();
105     this.mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
106   }
107   
108   /**
109    * Processes a entity count history search request
110    *
111    * @param exchange The Exchange object generated by Apache Camel for the incoming request
112    */
113
114   @Override
115   public void process(Exchange exchange) throws Exception {
116           
117           Request request = exchange.getIn().getHeader(RestletConstants.RESTLET_REQUEST, Request.class);
118             Response restletResponse =
119                 exchange.getIn().getHeader(RestletConstants.RESTLET_RESPONSE, Response.class);
120
121             Object xTransactionId = exchange.getIn().getHeader("X-TransactionId");
122             if (xTransactionId == null) {
123               xTransactionId = NodeUtils.getRandomTxnId();
124             }
125
126             Object partnerName = exchange.getIn().getHeader("X-FromAppId");
127             if (partnerName == null) {
128               partnerName = "Browser";
129             }
130
131             /*
132              * Disables automatic Apache Camel Restlet component logging which prints out an undesirable log
133              * entry which includes client (e.g. browser) information
134              */
135             request.setLoggable(false);
136
137             ClientInfo clientInfo = request.getClientInfo();
138             MdcContext.initialize((String) xTransactionId, "AAI-UI", "", (String) partnerName,
139                 clientInfo.getAddress() + ":" + clientInfo.getPort());
140
141             String typeParameter = getTypeParameter(exchange);
142
143             if (null != typeParameter && !typeParameter.isEmpty()) {
144               OperationResult operationResult = null;
145
146               try {
147                 operationResult = getResults(restletResponse, typeParameter);
148                 restletResponse.setEntity(operationResult.getResult(), MediaType.APPLICATION_JSON);
149               } catch (Exception exc) {
150                 LOG.error(AaiUiMsgs.CONFIGURATION_ERROR, exc.getLocalizedMessage());
151               }
152             } else {
153               LOG.error(AaiUiMsgs.RESOURCE_NOT_FOUND, request.getOriginalRef().toString());
154               String errorMessage =
155                   restletUtils.generateJsonErrorResponse("Unsupported request. Resource not found.");
156               restletResponse.setEntity(errorMessage, MediaType.APPLICATION_JSON);
157               restletResponse.setStatus(Status.CLIENT_ERROR_NOT_FOUND);
158             }
159
160             exchange.getOut().setBody(restletResponse);
161   }
162
163
164   /**
165    * Format line graph output
166    *
167    * @param results The results
168    * @return The JSON object
169    * @throws JsonProcessingException The JSON processing exception
170    */
171   public JSONObject formatLineGraphOutput(String results) throws JsonProcessingException {
172     Map<Long, Long> countByDateMap = new HashMap<Long, Long>();
173
174     JsonNode resultNode = null;
175
176     JSONObject finalResult = new JSONObject();
177     JSONArray finalResultArr = new JSONArray();
178
179     try {
180       resultNode = mapper.readTree(results);
181
182       final JsonNode bucketsNode = getBucketsNode(resultNode);
183
184       if (bucketsNode.isArray()) {
185
186         for (final JsonNode entityNode : bucketsNode) {
187           final JsonNode dateBucketNode = entityNode.get("group_by_date").get("buckets");
188           if (dateBucketNode.isArray()) {
189             for (final JsonNode dateBucket : dateBucketNode) {
190               Long date = dateBucket.get("key").asLong();
191               final JsonNode countBucketNode =
192                   dateBucket.get("sort_by_date").get("hits").get("hits");
193
194               if (countBucketNode.isArray()) {
195                 final JsonNode latestEntityNode = countBucketNode.get(0);
196
197                 long currentCount = latestEntityNode.get("_source").get("count").asLong();
198                 if (countByDateMap.containsKey(date)) {
199                   // add to the value if map already contains this date
200                   currentCount += countByDateMap.get(date);
201                 }
202
203                 countByDateMap.put(date, currentCount);
204               }
205             }
206
207           }
208         }
209       }
210
211       /*
212        * Sort the map by epoch timestamp
213        */
214       Map<Long, Long> sortedMap = new TreeMap<Long, Long>(countByDateMap);
215       for (Entry<Long, Long> entry : sortedMap.entrySet()) {
216         JSONObject dateEntry = new JSONObject();
217         dateEntry.put("date", entry.getKey());
218         dateEntry.put("count", entry.getValue());
219         finalResultArr.put(dateEntry);
220       }
221
222     } catch (Exception exc) {
223       LOG.warn(AaiUiMsgs.ERROR_BUILDING_SEARCH_RESPONSE, exc.getLocalizedMessage());
224     }
225
226     return finalResult.put("result", finalResultArr);
227   }
228
229   /**
230    * Format table output
231    *
232    * @param results The results
233    * @return The JSON object
234    * @throws JsonProcessingException The JSON processing exception
235    */
236   public JSONObject formatTableOutput(String results) throws JsonProcessingException {
237     JsonNode resultNode = null;
238
239     JSONObject finalResult = new JSONObject();
240     JSONArray entitiesArr = new JSONArray();
241
242     Map<String, Long> entityCountInTable = initializeEntityMap();
243
244     long vnfCount = 0;
245
246     try {
247       resultNode = mapper.readTree(results);
248
249       final JsonNode bucketsNode = getBucketsNode(resultNode);
250       if (bucketsNode.isArray()) {
251
252         for (final JsonNode entityNode : bucketsNode) {
253           String entityType = entityNode.get("key").asText();
254           boolean isAVnf = vnfEntityTypes.contains(entityType);
255           long countValue = 0;
256
257           if (isAVnf || entityCountInTable.get(entityType) != null) {
258             final JsonNode hitsBucketNode = entityNode.get("sort_by_date").get("hits").get("hits");
259             if (hitsBucketNode.isArray()) {
260               // the first bucket will be the latest
261               final JsonNode hitNode = hitsBucketNode.get(0);
262
263               countValue = hitNode.get("_source").get("count").asLong();
264
265               /*
266                * Special case: Add all the VNF types together to get aggregate count
267                */
268               if (summarizeVnfs && isAVnf) {
269                 vnfCount += countValue;
270                 countValue = vnfCount;
271                 entityType = "vnf";
272               }
273
274               entityCountInTable.replace(entityType, countValue);
275             }
276           }
277
278         }
279       }
280       for (Entry<String, Long> entry : entityCountInTable.entrySet()) {
281         JSONObject entityType = new JSONObject();
282         entityType.put("key", entry.getKey());
283         entityType.put("doc_count", entry.getValue());
284         entitiesArr.put(entityType);
285       }
286
287       finalResult.put("result", entitiesArr);
288
289     } catch (Exception exc) {
290       LOG.warn(AaiUiMsgs.ERROR_BUILDING_RESPONSE_FOR_TABLE_QUERY, exc.getLocalizedMessage());
291     }
292
293     return finalResult;
294   }
295
296   /**
297    * Gets the results
298    *
299    * @param response The response
300    * @param type The type
301    * @return The results
302    */
303   public OperationResult getResults(Response response, String type) {
304     OperationResult operationResult = new OperationResult();
305
306     String reqPayload = EntityHistoryQueryBuilder.getQuery(type).toString();
307
308     try {
309       final String fullUrlStr = elasticSearchAdapter
310           .buildElasticSearchUrlForApi(entityCountHistoryIndexName, SEARCH_PRETTY_STRING);
311
312       OperationResult opResult = elasticSearchAdapter.doPost(fullUrlStr, reqPayload,
313           javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE);
314
315       JSONObject finalOutput = null;
316       if (type.equalsIgnoreCase(TABLE)) {
317         finalOutput = formatTableOutput(opResult.getResult());
318       } else if (type.equalsIgnoreCase(GRAPH)) {
319         finalOutput = formatLineGraphOutput(opResult.getResult());
320       }
321
322       if (finalOutput != null) {
323         response.setEntity(finalOutput.toString(), MediaType.APPLICATION_JSON);
324         operationResult.setResult(finalOutput.toString());
325       }
326     } catch (JsonProcessingException exc) {
327       restletUtils.handleRestletErrors(LOG, "Unable to map JSONpayload", exc, response);
328     }
329
330     return operationResult;
331   }
332
333   /**
334    * Gets the buckets node
335    *
336    * @param node The node
337    * @return The buckets node
338    * @throws Exception The exception
339    */
340   public JsonNode getBucketsNode(JsonNode node) throws Exception {
341     if (node.get("aggregations").get("group_by_entityType").get("buckets") != null) {
342       return node.get("aggregations").get("group_by_entityType").get("buckets");
343     } else {
344       throw new Exception("Failed to map JSON response");
345     }
346   }
347
348   /**
349    * Initialize entity map
350    *
351    * @return the map
352    */
353   private Map<String, Long> initializeEntityMap() {
354     Map<String, Long> entityMap = new HashMap<String, Long>();
355     for (String entity : entityTypesToSummarize) {
356       entityMap.put(entity, (long) 0);
357     }
358
359     return entityMap;
360   }
361
362   /**
363    * Extracts the "type" query parameter from the request URI
364    *
365    * @param exchange
366    * @return String containing the value of the "type" query parameter of the request. Returns null
367    *         if no "type" parameter found
368    */
369   public String getTypeParameter(Exchange exchange) {
370     String typeParameter = null;
371
372     String requestUriParameterString = exchange.getIn().getHeader("CamelHttpQuery", String.class);
373
374     if (null != requestUriParameterString) {
375       String[] requestParameterParts = requestUriParameterString.split("&");
376
377       String[] parameter = requestParameterParts[0].split("=");
378       String currentParameterKey = parameter[0];
379
380       if (null != currentParameterKey && !currentParameterKey.isEmpty()) {
381         // Check if we're looking at the "type" parameter key
382         if (currentParameterKey.equals(TYPE)) {
383           boolean uriIncludesTypeParameterValue =
384               (parameter.length >= 2) && !parameter[1].isEmpty();
385
386           if (uriIncludesTypeParameterValue) {
387             String typeParameterValue = parameter[1];
388
389             // Is the parameter value one that we return data for?
390             if (typeParameterValue.equalsIgnoreCase(TABLE)
391                 || typeParameterValue.equalsIgnoreCase(GRAPH)) {
392               typeParameter = typeParameterValue;
393             }
394           }
395         }
396       }
397     }
398
399     return typeParameter;
400   }
401
402
403   public void setRestletUtils(RestletUtils restletUtils) {
404     this.restletUtils = restletUtils;
405   }
406
407 }