Adding more sparky junit coverage
[aai/sparky-be.git] / src / main / java / org / onap / aai / sparky / util / NodeUtils.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017 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  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  */
23 package org.onap.aai.sparky.util;
24
25 import java.io.BufferedReader;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.lang.Thread.UncaughtExceptionHandler;
30 import java.net.URI;
31 import java.nio.ByteBuffer;
32 import java.security.SecureRandom;
33 import java.sql.Timestamp;
34 import java.text.ParseException;
35 import java.text.SimpleDateFormat;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.Date;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.TimeZone;
45 import java.util.concurrent.ExecutorService;
46 import java.util.concurrent.Executors;
47 import java.util.concurrent.ThreadFactory;
48 import java.util.concurrent.TimeUnit;
49 import java.util.regex.Matcher;
50 import java.util.regex.Pattern;
51
52 import javax.servlet.http.HttpServletRequest;
53 import javax.xml.stream.XMLStreamConstants;
54
55 import org.onap.aai.cl.api.Logger;
56 import org.onap.aai.sparky.logging.AaiUiMsgs;
57 import org.onap.aai.sparky.viewandinspect.config.SparkyConstants;
58 import org.restlet.Request;
59
60 import com.fasterxml.jackson.core.JsonProcessingException;
61 import com.fasterxml.jackson.databind.JsonNode;
62 import com.fasterxml.jackson.databind.ObjectMapper;
63 import com.fasterxml.jackson.databind.ObjectWriter;
64 import com.fasterxml.jackson.databind.SerializationFeature;
65 import com.fasterxml.jackson.databind.ser.FilterProvider;
66 import com.google.common.util.concurrent.ThreadFactoryBuilder;
67
68
69 /**
70  * The Class NodeUtils.
71  */
72 public class NodeUtils {
73   private static SecureRandom sRandom = new SecureRandom();
74   
75   private static final Pattern AAI_VERSION_PREFIX = Pattern.compile("/aai/v[0-9]+/(.*)");
76
77   
78   public static synchronized String getRandomTxnId(){
79       byte bytes[] = new byte[6];
80       sRandom.nextBytes(bytes);
81       return Integer.toUnsignedString(ByteBuffer.wrap(bytes).getInt());
82   }
83
84   /**
85    * Builds the depth padding.
86    *
87    * @param depth the depth
88    * @return the string
89    */
90   public static String buildDepthPadding(int depth) {
91     StringBuilder sb = new StringBuilder(32);
92
93     for (int x = 0; x < depth; x++) {
94       sb.append("   ");
95     }
96
97     return sb.toString();
98   }
99   
100   
101   public static String extractRawPathWithoutVersion(String selfLinkUri) {
102
103     try {
104
105       String rawPath = new URI(selfLinkUri).getRawPath();
106       
107       Matcher m = AAI_VERSION_PREFIX.matcher(rawPath);
108
109       if (m.matches()) {
110
111         if ( m.groupCount() >= 1) {
112           return m.group(1);
113         }
114           
115       }
116     } catch (Exception e) {
117     }
118     
119     return null;
120     
121   }
122
123   /**
124    * Checks if is numeric.
125    *
126    * @param numberStr the number str
127    * @return true, if is numeric
128    */
129   public static boolean isNumeric(String numberStr) {
130
131     try {
132       Double.parseDouble(numberStr);
133     } catch (Exception exc) {
134       return false;
135     }
136
137     return true;
138
139   }
140
141   /**
142    * Creates the named executor.
143    *
144    * @param name the name
145    * @param numWorkers the num workers
146    * @param logger the logger
147    * @return the executor service
148    */
149   public static ExecutorService createNamedExecutor(String name, int numWorkers, final Logger logger) {
150     UncaughtExceptionHandler uncaughtExceptionHandler = new Thread.UncaughtExceptionHandler() {
151
152       @Override
153       public void uncaughtException(Thread thread, Throwable exc) {
154
155         logger.error(AaiUiMsgs.ERROR_GENERIC, thread.getName() + ": " + exc);
156
157       }
158     };
159
160     ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat(name + "-%d")
161         .setUncaughtExceptionHandler(uncaughtExceptionHandler).build();
162
163     return Executors.newScheduledThreadPool(numWorkers + 1, namedThreadFactory);
164   }
165
166   /**
167    * Calculate edit attribute uri.
168    *
169    * @param link the link
170    * @return the string
171    */
172   public static String calculateEditAttributeUri(String link) {
173     String uri = null;
174
175     if (link != null) {
176
177       Pattern pattern = Pattern.compile(SparkyConstants.URI_VERSION_REGEX_PATTERN);
178       Matcher matcher = pattern.matcher(link);
179       if (matcher.find()) {
180         uri = link.substring(matcher.end());
181       }
182     }
183     return uri;
184   }
185
186   /**
187    * Generate unique sha digest.
188    *
189    * @param keys the keys
190    * @return the string
191    */
192   public static String generateUniqueShaDigest(String... keys) {
193
194     if ((keys == null) || keys.length == 0) {
195       return null;
196     }
197
198     final String keysStr = Arrays.asList(keys).toString();
199     final String hashedId = org.apache.commons.codec.digest.DigestUtils.sha256Hex(keysStr);
200
201     return hashedId;
202   }
203
204   /**
205    * Gets the node field as text.
206    *
207    * @param node the node
208    * @param fieldName the field name
209    * @return the node field as text
210    */
211   public static String getNodeFieldAsText(JsonNode node, String fieldName) {
212
213     String fieldValue = null;
214
215     JsonNode valueNode = node.get(fieldName);
216
217     if (valueNode != null) {
218       fieldValue = valueNode.asText();
219     }
220
221     return fieldValue;
222   }
223
224   private static final String ENTITY_RESOURCE_KEY_FORMAT = "%s.%s";
225
226   /**
227    * Convert a millisecond duration to a string format
228    * 
229    * @param millis A duration to convert to a string form
230    * @return A string of the form "X Days Y Hours Z Minutes A Seconds".
231    */
232
233   private static final String TIME_BREAK_DOWN_FORMAT =
234       "[ %d days, %d hours, %d minutes, %d seconds ]";
235
236   /**
237    * Gets the duration breakdown.
238    *
239    * @param millis the millis
240    * @return the duration breakdown
241    */
242   public static String getDurationBreakdown(long millis) {
243
244     if (millis < 0) {
245       return String.format(TIME_BREAK_DOWN_FORMAT, 0, 0, 0, 0);
246     }
247
248     long days = TimeUnit.MILLISECONDS.toDays(millis);
249     millis -= TimeUnit.DAYS.toMillis(days);
250     long hours = TimeUnit.MILLISECONDS.toHours(millis);
251     millis -= TimeUnit.HOURS.toMillis(hours);
252     long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
253     millis -= TimeUnit.MINUTES.toMillis(minutes);
254     long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
255
256     return String.format(TIME_BREAK_DOWN_FORMAT, days, hours, minutes, seconds);
257
258   }
259
260   /**
261    * Checks if is equal.
262    *
263    * @param n1 the n 1
264    * @param n2 the n 2
265    * @return true, if is equal
266    */
267   public static boolean isEqual(JsonNode n1, JsonNode n2) {
268
269     /*
270      * due to the inherent nature of json being unordered, comparing object representations of the
271      * same keys and values but different order makes comparison challenging. Let's try an
272      * experiment where we compare the structure of the json, and then simply compare the sorted
273      * order of that structure which should be good enough for what we are trying to accomplish.
274      */
275
276     TreeWalker walker = new TreeWalker();
277     List<String> n1Paths = new ArrayList<String>();
278     List<String> n2Paths = new ArrayList<String>();
279
280     walker.walkTree(n1Paths, n1);
281     walker.walkTree(n2Paths, n2);
282
283     Collections.sort(n1Paths);
284     Collections.sort(n2Paths);
285
286     return n1Paths.equals(n2Paths);
287
288   }
289
290   /**
291    * Concat array.
292    *
293    * @param list the list
294    * @return the string
295    */
296   public static String concatArray(List<String> list) {
297     return concatArray(list, " ");
298   }
299   
300  private static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
301   
302   public static String getCurrentTimeStamp() {
303     SimpleDateFormat dateFormat = new SimpleDateFormat(TIMESTAMP_FORMAT);
304     Timestamp timestamp = new Timestamp(System.currentTimeMillis());
305     return dateFormat.format(timestamp);
306   }
307   
308   /**
309    * Concat array.
310    *
311    * @param list the list
312    * @param delimiter the delimiter
313    * @return the string
314    */
315   public static String concatArray(List<String> list, String delimiter) {
316
317     if (list == null || list.size() == 0) {
318       return "";
319     }
320
321     StringBuilder result = new StringBuilder(64);
322
323     boolean firstValue = true;
324
325     for (String item : list) {
326
327       if (firstValue) {
328         result.append(item);
329         firstValue = false;
330       } else {
331         result.append(delimiter).append(item);
332       }
333
334     }
335
336     return result.toString();
337
338   }
339
340   /**
341    * Concat array.
342    *
343    * @param values the values
344    * @return the string
345    */
346   public static String concatArray(String[] values) {
347
348     if (values == null || values.length == 0) {
349       return "";
350     }
351
352     StringBuilder result = new StringBuilder(64);
353
354     boolean firstValue = true;
355
356     for (String item : values) {
357
358       if (firstValue) {
359         result.append(item);
360         firstValue = false;
361       } else {
362         result.append(".").append(item);
363       }
364
365     }
366
367     return result.toString();
368
369   }
370
371   /**
372    * Builds the entity resource key.
373    *
374    * @param entityType the entity type
375    * @param resourceId the resource id
376    * @return the string
377    */
378   public static String buildEntityResourceKey(String entityType, String resourceId) {
379     return String.format(ENTITY_RESOURCE_KEY_FORMAT, entityType, resourceId);
380   }
381
382   /**
383    * Extract resource id from link.
384    *
385    * @param link the link
386    * @return the string
387    */
388   public static String extractResourceIdFromLink(String link) {
389
390     if (link == null) {
391       return null;
392     }
393
394     int linkLength = link.length();
395     if (linkLength == 0) {
396       return null;
397     }
398
399     /*
400      * if the last character != / then we need to change the lastIndex position
401      */
402
403     int startIndex = 0;
404     String resourceId = null;
405     if ("/".equals(link.substring(linkLength - 1))) {
406       // Use-case:
407       // https://ext1.test.onap.com:9292/aai/v7/business/customers/customer/customer-1/service-subscriptions/service-subscription/service-subscription-1/
408       startIndex = link.lastIndexOf("/", linkLength - 2);
409       resourceId = link.substring(startIndex + 1, linkLength - 1);
410     } else {
411       // Use-case:
412       // https://ext1.test.onap.com:9292/aai/v7/business/customers/customer/customer-1/service-subscriptions/service-subscription/service-subscription-1
413       startIndex = link.lastIndexOf("/");
414       resourceId = link.substring(startIndex + 1, linkLength);
415     }
416
417     String result = null;
418
419     if (resourceId != null) {
420       try {
421         result = java.net.URLDecoder.decode(resourceId, "UTF-8");
422       } catch (Exception exc) {
423         /*
424          * if there is a failure decoding the parameter we will just return the original value.
425          */
426         result = resourceId;
427       }
428     }
429
430     return result;
431
432   }
433
434   /**
435    * Gets the xml stream constant as str.
436    *
437    * @param value the value
438    * @return the xml stream constant as str
439    */
440   public static String getXmlStreamConstantAsStr(int value) {
441     switch (value) {
442       case XMLStreamConstants.ATTRIBUTE:
443         return "ATTRIBUTE";
444       case XMLStreamConstants.CDATA:
445         return "CDATA";
446       case XMLStreamConstants.CHARACTERS:
447         return "CHARACTERS";
448       case XMLStreamConstants.COMMENT:
449         return "COMMENT";
450       case XMLStreamConstants.DTD:
451         return "DTD";
452       case XMLStreamConstants.END_DOCUMENT:
453         return "END_DOCUMENT";
454       case XMLStreamConstants.END_ELEMENT:
455         return "END_ELEMENT";
456       case XMLStreamConstants.ENTITY_DECLARATION:
457         return "ENTITY_DECLARATION";
458       case XMLStreamConstants.ENTITY_REFERENCE:
459         return "ENTITY_REFERENCE";
460       case XMLStreamConstants.NAMESPACE:
461         return "NAMESPACE";
462       case XMLStreamConstants.NOTATION_DECLARATION:
463         return "NOTATION_DECLARATION";
464       case XMLStreamConstants.PROCESSING_INSTRUCTION:
465         return "PROCESSING_INSTRUCTION";
466       case XMLStreamConstants.SPACE:
467         return "SPACE";
468       case XMLStreamConstants.START_DOCUMENT:
469         return "START_DOCUMENT";
470       case XMLStreamConstants.START_ELEMENT:
471         return "START_ELEMENT";
472
473       default:
474         return "Unknown(" + value + ")";
475     }
476   }
477
478   /**
479    * Convert object to json.
480    *
481    * @param object the object
482    * @param pretty the pretty
483    * @return the string
484    * @throws JsonProcessingException the json processing exception
485    */
486   public static String convertObjectToJson(Object object, boolean pretty)
487       throws JsonProcessingException {
488     ObjectWriter ow = null;
489
490     ObjectMapper mapper = new ObjectMapper();
491     mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
492     
493     if (pretty) {
494       ow = mapper.writer().withDefaultPrettyPrinter();
495
496     } else {
497       ow = mapper.writer();
498     }
499
500     return ow.writeValueAsString(object);
501   }
502   
503   /**
504    * Convert object to json by selectively choosing certain fields thru filters.
505    * Example use case: 
506    * based on request type we might need to send different serialization of the UiViewFilterEntity
507    *
508    * @param object the object
509    * @param pretty the pretty
510    * @return the string
511    * @throws JsonProcessingException the json processing exception
512    */
513   public static String convertObjectToJson(Object object, boolean pretty, FilterProvider filters)
514       throws JsonProcessingException {
515     ObjectWriter ow = null;
516
517     ObjectMapper mapper = new ObjectMapper();
518     mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
519     
520     if (pretty) {
521       ow = mapper.writer(filters).withDefaultPrettyPrinter();
522
523     } else {
524       ow = mapper.writer(filters);
525     }
526
527     return ow.writeValueAsString(object);
528   }
529   
530
531   /**
532    * Convert json str to json node.
533    *
534    * @param jsonStr the json str
535    * @return the json node
536    * @throws IOException Signals that an I/O exception has occurred.
537    */
538   public static JsonNode convertJsonStrToJsonNode(String jsonStr) throws IOException {
539     ObjectMapper mapper = new ObjectMapper();
540     if (jsonStr == null || jsonStr.length() == 0) {
541       return null;
542     }
543
544     return mapper.readTree(jsonStr);
545   }
546
547   /**
548    * Convert object to xml.
549    *
550    * @param object the object
551    * @return the string
552    * @throws JsonProcessingException the json processing exception
553    */
554   public static String convertObjectToXml(Object object) throws JsonProcessingException {
555     ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
556     String jsonOutput = ow.writeValueAsString(object);
557
558     if (jsonOutput == null) {
559       return null;
560     }
561
562     return JsonXmlConverter.convertJsontoXml(jsonOutput);
563
564   }
565
566   /**
567    * Extract objects by key.
568    *
569    * @param node the node
570    * @param searchKey the search key
571    * @param foundObjects the found objects
572    */
573   public static void extractObjectsByKey(JsonNode node, String searchKey,
574       Collection<JsonNode> foundObjects) {
575
576     if ( node == null ) {
577       return;
578     }
579     
580     if (node.isObject()) {
581       Iterator<Map.Entry<String, JsonNode>> nodeIterator = node.fields();
582
583       while (nodeIterator.hasNext()) {
584         Map.Entry<String, JsonNode> entry = nodeIterator.next();
585         if (!entry.getValue().isValueNode()) {
586           extractObjectsByKey(entry.getValue(), searchKey, foundObjects);
587         }
588
589         String name = entry.getKey();
590         if (name.equalsIgnoreCase(searchKey)) {
591
592           JsonNode entryNode = entry.getValue();
593
594           if (entryNode.isArray()) {
595
596             Iterator<JsonNode> arrayItemsIterator = entryNode.elements();
597             while (arrayItemsIterator.hasNext()) {
598               foundObjects.add(arrayItemsIterator.next());
599             }
600
601           } else {
602             foundObjects.add(entry.getValue());
603           }
604
605
606         }
607       }
608     } else if (node.isArray()) {
609       Iterator<JsonNode> arrayItemsIterator = node.elements();
610       while (arrayItemsIterator.hasNext()) {
611         extractObjectsByKey(arrayItemsIterator.next(), searchKey, foundObjects);
612       }
613
614     }
615
616   }
617
618   /**
619    * Convert array into list.
620    *
621    * @param node the node
622    * @param instances the instances
623    */
624   public static void convertArrayIntoList(JsonNode node, Collection<JsonNode> instances) {
625
626     if (node.isArray()) {
627       Iterator<JsonNode> arrayItemsIterator = node.elements();
628       while (arrayItemsIterator.hasNext()) {
629         instances.add(arrayItemsIterator.next());
630       }
631
632     } else {
633       instances.add(node);
634     }
635
636   }
637
638   /**
639    * Extract field values from object.
640    *
641    * @param node the node
642    * @param attributesToExtract the attributes to extract
643    * @param fieldValues the field values
644    */
645   public static void extractFieldValuesFromObject(JsonNode node,
646       Collection<String> attributesToExtract, Collection<String> fieldValues) {
647
648     if (node == null) {
649       return;
650     }
651
652     if (node.isObject()) {
653
654       JsonNode valueNode = null;
655
656       for (String attrToExtract : attributesToExtract) {
657
658         valueNode = node.get(attrToExtract);
659
660         if (valueNode != null) {
661
662           if (valueNode.isValueNode()) {
663             fieldValues.add(valueNode.asText());
664           }
665         }
666       }
667     }
668   }
669
670   /**
671    * Extract field value from object.
672    *
673    * @param node the node
674    * @param fieldName the field name
675    * @return the string
676    */
677   public static String extractFieldValueFromObject(JsonNode node, String fieldName) {
678
679     if (node == null) {
680       return null;
681     }
682
683     if (node.isObject()) {
684
685       JsonNode valueNode = node.get(fieldName);
686
687       if (valueNode != null) {
688
689         if (valueNode.isValueNode()) {
690           return valueNode.asText();
691         }
692       }
693
694     }
695     return null;
696
697   }
698
699   /**
700    * Format timestamp.
701    *
702    * @param timestamp the timestamp
703    * @return the string
704    */
705   public static String formatTimestamp(String timestamp) {
706     try {
707       SimpleDateFormat originalFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
708       originalFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
709       Date toDate = originalFormat.parse(timestamp);
710       SimpleDateFormat newFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
711       newFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
712       return newFormat.format(toDate);
713
714     } catch (ParseException pe) {
715       return timestamp;
716     }
717   }
718  
719   /**
720    * Gets the HttpRequest payload.
721    *
722    * @param request the request
723    * @return the body
724    * @throws IOException Signals that an I/O exception has occurred.
725    */
726   public static String getBody(HttpServletRequest request) throws IOException {
727     InputStream inputStream = request.getInputStream();
728     return getBodyFromStream(inputStream);
729   }
730   
731   
732
733   /**
734    * Gets the Restlet Request payload.
735    *
736    * @param request the request
737    * @return the body
738    * @throws IOException Signals that an I/O exception has occurred.
739    */
740   public static String getBody(Request request) throws IOException {
741     InputStream inputStream = request.getEntity().getStream();
742     return getBodyFromStream(inputStream);
743   }
744   
745
746   /**
747    * Gets the payload from the input stream of a request.
748    *
749    * @param request the request
750    * @return the body
751    * @throws IOException Signals that an I/O exception has occurred.
752    */
753   public static String getBodyFromStream(InputStream inputStream) throws IOException {
754
755     String body = null;
756     StringBuilder stringBuilder = new StringBuilder();
757     BufferedReader bufferedReader = null;
758
759     try {
760       if (inputStream != null) {
761         bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
762         char[] charBuffer = new char[128];
763         int bytesRead = -1;
764         while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
765           stringBuilder.append(charBuffer, 0, bytesRead);
766         }
767       } else {
768         stringBuilder.append("");
769       }
770     } catch (IOException ex) {
771       throw ex;
772     } finally {
773       if (bufferedReader != null) {
774         try {
775           bufferedReader.close();
776         } catch (IOException ex) {
777           throw ex;
778         }
779       }
780     }
781
782     body = stringBuilder.toString();
783     return body;
784   }
785
786   
787   /**
788    * The main method.
789    *
790    * @param args the arguments
791    * @throws ParseException the parse exception
792    */
793   public static void main(String[] args) throws ParseException {
794     String date = "20170110T112312Z";
795     SimpleDateFormat originalFormat = new SimpleDateFormat("yyyyMMdd'T'hhmmss'Z'");
796     Date toDate = originalFormat.parse(date);
797     SimpleDateFormat newFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss'Z'");
798     System.out.println(newFormat.format(toDate));
799
800   }
801
802
803
804 }