Initial commit for AAI-UI(sparky-backend)
[aai/sparky-be.git] / src / main / java / org / openecomp / sparky / viewandinspect / entity / ActiveInventoryNode.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
26 package org.openecomp.sparky.viewandinspect.entity;
27
28 import java.io.IOException;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Map.Entry;
36 import java.util.Set;
37 import java.util.concurrent.ConcurrentLinkedDeque;
38 import java.util.concurrent.atomic.AtomicBoolean;
39 import java.util.regex.Matcher;
40 import java.util.regex.Pattern;
41
42 import org.openecomp.cl.api.Logger;
43 import org.openecomp.cl.eelf.LoggerFactory;
44 import org.openecomp.sparky.config.oxm.OxmModelLoader;
45 import org.openecomp.sparky.dal.rest.OperationResult;
46 import org.openecomp.sparky.logging.AaiUiMsgs;
47 import org.openecomp.sparky.viewandinspect.config.VisualizationConfig;
48 import org.openecomp.sparky.viewandinspect.enumeration.NodeProcessingAction;
49 import org.openecomp.sparky.viewandinspect.enumeration.NodeProcessingState;
50
51 import com.fasterxml.jackson.databind.JsonNode;
52 import com.fasterxml.jackson.databind.ObjectMapper;
53
54 /**
55  * The Class ActiveInventoryNode.
56  */
57 public class ActiveInventoryNode {
58
59   private static final Logger LOG = LoggerFactory.getInstance().getLogger(
60       ActiveInventoryNode.class);
61   private static final String URIRegexPattern = "aai/v[\\d]/";
62
63   public static final int DEFAULT_INIT_NODE_DEPTH = 1000;
64
65   private String nodeId;
66   private String selfLink;
67
68   private boolean isRootNode;
69   private ConcurrentLinkedDeque<String> inboundNeighbors;
70   private ConcurrentLinkedDeque<String> outboundNeighbors;
71   private List<JsonNode> complexGroups;
72   private List<RelationshipList> relationshipLists;
73   private int nodeDepth;
74   private OperationResult opResult;
75
76
77   private boolean processingErrorOccurred;
78   private List<String> errorCauses;
79   private boolean selflinkRetrievalFailure;
80   private NodeProcessingState state;
81
82   private boolean processedNeighbors;
83
84   private boolean selfLinkPendingResolve;
85   
86   /*
87    * I think we shouldn't be using this crutch flags.  If these things are meant
88    * to represent the current state of the node, then they should be legitimate 
89    * state transitions.
90    */
91   
92   private boolean selfLinkDeterminationPending;
93
94   private AtomicBoolean selfLinkProcessed;
95
96   private OxmModelLoader oxmModelLoader;
97   private VisualizationConfig visualizationConfig;
98
99   private String entityType;
100   private String primaryKeyName;
101   private String primaryKeyValue;
102
103   private boolean nodeIssue;
104   private boolean ignoredByFilter;
105
106   private boolean resolvedSelfLink;
107
108   private Map<String, String> properties;
109   private ArrayList<String> queryParams;
110
111   private ObjectMapper mapper;
112
113   /**
114    * Instantiates a new active inventory node.
115    */
116   public ActiveInventoryNode() {
117     this(null);
118   }
119
120   /**
121    * Instantiates a new active inventory node.
122    *
123    * @param key the key
124    */
125   public ActiveInventoryNode(String key) {
126     this.nodeId = null;
127     this.entityType = null;
128     this.selfLink = null;
129     this.properties = new HashMap<String, String>();
130     this.processingErrorOccurred = false;
131     this.errorCauses = new ArrayList<String>();
132     this.selflinkRetrievalFailure = false;
133     this.nodeIssue = false;
134     this.state = NodeProcessingState.INIT;
135     this.selfLinkPendingResolve = false;
136     this.selfLinkDeterminationPending = false;
137
138     selfLinkProcessed = new AtomicBoolean(Boolean.FALSE);
139     oxmModelLoader = null;
140     visualizationConfig = null;
141
142     isRootNode = false;
143     inboundNeighbors = new ConcurrentLinkedDeque<String>();
144     outboundNeighbors = new ConcurrentLinkedDeque<String>();
145     complexGroups = new ArrayList<JsonNode>();
146     relationshipLists = new ArrayList<RelationshipList>();
147     nodeDepth = DEFAULT_INIT_NODE_DEPTH;
148     queryParams = new ArrayList<String>();
149
150     mapper = new ObjectMapper();
151
152     processedNeighbors = false;
153     resolvedSelfLink = false;
154
155
156   }
157   
158   public void clearQueryParams() {
159     queryParams.clear();
160   }
161   
162   public void addQueryParam(String queryParam) {
163     if ( queryParam!= null) {
164       if( !queryParams.contains(queryParam)) {
165         queryParams.add(queryParam);
166       }
167     }
168   }
169   
170   public void addQueryParams(Collection<String> params) {
171
172     if (params != null & params.size() > 0) {
173
174       for (String param : params) {
175         addQueryParam(param);
176       }
177     }
178   }
179
180   
181   public List<String> getQueryParams() {
182     return queryParams;
183   }
184
185   public void setSelfLinkDeterminationPending(boolean selfLinkDeterminationPending) {
186     this.selfLinkDeterminationPending = selfLinkDeterminationPending;
187   }
188
189   public boolean isSelfLinkDeterminationPending() {
190     return selfLinkDeterminationPending;
191   }
192
193   public NodeProcessingState getState() {
194     return state;
195   }
196
197   public List<JsonNode> getComplexGroups() {
198     return complexGroups;
199   }
200
201   public List<RelationshipList> getRelationshipLists() {
202     return relationshipLists;
203   }
204
205   public OperationResult getOpResult() {
206     return opResult;
207   }
208
209   public void setOpResult(OperationResult opResult) {
210     this.opResult = opResult;
211   }
212
213   public String getPrimaryKeyName() {
214     return primaryKeyName;
215   }
216
217   /**
218    * Gets the visualization config.
219    *
220    * @return the visualization config
221    */
222   public VisualizationConfig getvisualizationConfig() {
223     return visualizationConfig;
224   }
225
226   public int getNodeDepth() {
227     return nodeDepth;
228   }
229
230   public void setNodeDepth(int nodeDepth) {
231     this.nodeDepth = nodeDepth;
232   }
233
234   /**
235    * Sets the visualization config.
236    *
237    * @param visualizationConfig the new visualization config
238    */
239   public void setvisualizationConfig(VisualizationConfig visualizationConfig) {
240     this.visualizationConfig = visualizationConfig;
241   }
242
243   public OxmModelLoader getOxmModelLoader() {
244     return oxmModelLoader;
245   }
246
247   public void setPrimaryKeyName(String primaryKeyName) {
248     this.primaryKeyName = primaryKeyName;
249   }
250
251   public String getPrimaryKeyValue() {
252     return primaryKeyValue;
253   }
254
255   public void setPrimaryKeyValue(String primaryKeyValue) {
256     this.primaryKeyValue = primaryKeyValue;
257   }
258
259   public boolean isNodeIssue() {
260     return nodeIssue;
261   }
262
263   public boolean isIgnoredByFilter() {
264     return ignoredByFilter;
265   }
266
267   public void setIgnoredByFilter(boolean ignoredByFilter) {
268     this.ignoredByFilter = ignoredByFilter;
269   }
270
271   public void setNodeIssue(boolean nodeIssue) {
272     this.nodeIssue = nodeIssue;
273   }
274
275   /**
276    * Checks for processed neighbors.
277    *
278    * @return true, if successful
279    */
280   public boolean hasProcessedNeighbors() {
281     return processedNeighbors;
282   }
283
284   public void setProcessedNeighbors(boolean processedNeighbors) {
285     this.processedNeighbors = processedNeighbors;
286   }
287
288   /**
289    * Checks for resolved self link.
290    *
291    * @return true, if successful
292    */
293   public boolean hasResolvedSelfLink() {
294     return resolvedSelfLink;
295   }
296
297   public void setResolvedSelfLink(boolean resolvedSelfLink) {
298     this.resolvedSelfLink = resolvedSelfLink;
299   }
300
301   /**
302    * Checks for neighbors.
303    *
304    * @return true, if successful
305    */
306   public boolean hasNeighbors() {
307     return (inboundNeighbors.size() > 0 || outboundNeighbors.size() > 0);
308   }
309
310   /**
311    * Adds the inbound neighbor.
312    *
313    * @param nodeId the node id
314    */
315   public void addInboundNeighbor(String nodeId) {
316
317     if (nodeId == null) {
318       return;
319     }
320
321     if (!inboundNeighbors.contains(nodeId)) {
322       inboundNeighbors.add(nodeId);
323     }
324
325   }
326
327   /**
328    * Adds the outbound neighbor.
329    *
330    * @param nodeId the node id
331    */
332   public void addOutboundNeighbor(String nodeId) {
333
334     if (nodeId == null) {
335       return;
336     }
337
338     if (!outboundNeighbors.contains(nodeId)) {
339       outboundNeighbors.add(nodeId);
340     }
341
342   }
343
344   public boolean isAtMaxDepth() {
345     return (nodeDepth >= VisualizationConfig.getConfig().getMaxSelfLinkTraversalDepth());
346   }
347
348   public ConcurrentLinkedDeque<String> getInboundNeighbors() {
349     return inboundNeighbors;
350   }
351
352   public void setInboundNeighbors(ConcurrentLinkedDeque<String> inboundNeighbors) {
353     this.inboundNeighbors = inboundNeighbors;
354   }
355
356   public Collection<String> getOutboundNeighbors() {
357     List<String> result = new ArrayList<String>();
358
359     Iterator<String> neighborIterator = outboundNeighbors.iterator();
360
361     while (neighborIterator.hasNext()) {
362       result.add(neighborIterator.next());
363     }
364
365     return result;
366   }
367
368   /**
369    * Change depth.
370    *
371    * @param newDepth the new depth
372    * @return true, if successful
373    */
374   public boolean changeDepth(int newDepth) {
375
376     boolean nodeDepthWasChanged = false;
377
378     if (newDepth < nodeDepth) {
379       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_DEPTH, nodeId,
380           String.valueOf(this.nodeDepth), String.valueOf(newDepth));
381       this.nodeDepth = newDepth;
382       nodeDepthWasChanged = true;
383     }
384
385     return nodeDepthWasChanged;
386
387   }
388
389   public void setOutboundNeighbors(ConcurrentLinkedDeque<String> outboundNeighbors) {
390     this.outboundNeighbors = outboundNeighbors;
391   }
392
393   public boolean isRootNode() {
394     return isRootNode;
395   }
396
397   public void setRootNode(boolean isRootNode) {
398     this.isRootNode = isRootNode;
399   }
400
401   /**
402    * Change state.
403    *
404    * @param newState the new state
405    * @param action the action
406    */
407   public void changeState(NodeProcessingState newState, NodeProcessingAction action) {
408     /*
409      * NodeId may be null depending on the current node life-cycle state 
410      */
411     
412     if (getNodeId() != null) {
413       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE, state.toString(), newState.toString(), action.toString());
414     } else {
415       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE_NO_NODE_ID, state.toString(), newState.toString(), action.toString());
416     }
417     this.state = newState;
418   }
419
420   public boolean isSelfLinkPendingResolve() {
421     return selfLinkPendingResolve;
422   }
423
424   public void setSelfLinkPendingResolve(boolean selfLinkPendingResolve) {
425     this.selfLinkPendingResolve = selfLinkPendingResolve;
426   }
427
428   public boolean isSelflinkRetrievalFailure() {
429     return selflinkRetrievalFailure;
430   }
431
432   public void setSelflinkRetrievalFailure(boolean selflinkRetrievalFailure) {
433     this.selflinkRetrievalFailure = selflinkRetrievalFailure;
434   }
435
436   public void setOxmModelLoader(OxmModelLoader loader) {
437     this.oxmModelLoader = loader;
438   }
439
440   public boolean getSelfLinkProcessed() {
441     return selfLinkProcessed.get();
442   }
443
444   public void setSelfLinkProcessed(boolean selfLinkProcessed) {
445     this.selfLinkProcessed.set(selfLinkProcessed);
446   }
447
448   public boolean isDirectSelfLink() {
449     // https://aai-int1.test.att.com:8443/aai/v8/resources/id/2458124400
450     return isDirectSelfLink(this.selfLink);
451   }
452
453   /**
454    * Checks if is direct self link.
455    *
456    * @param link the link
457    * @return true, if is direct self link
458    */
459   public static boolean isDirectSelfLink(String link) {
460     // https://aai-int1.test.att.com:8443/aai/v8/resources/id/2458124400
461
462     if (link == null) {
463       return false;
464     }
465
466     return link.contains("/resources/id/");
467
468   }
469
470   public Map<String, String> getProperties() {
471     return properties;
472   }
473
474   /**
475    * Adds the error cause.
476    *
477    * @param error the error
478    */
479   public void addErrorCause(String error) {
480     if (!errorCauses.contains(error)) {
481       errorCauses.add(error);
482     }
483   }
484
485   /**
486    * Adds the property.
487    *
488    * @param key the key
489    * @param value the value
490    */
491   public void addProperty(String key, String value) {
492     properties.put(key, value);
493   }
494
495   public boolean isProcessingErrorOccurred() {
496     return processingErrorOccurred;
497   }
498
499   public void setProcessingErrorOccurred(boolean processingErrorOccurred) {
500     this.processingErrorOccurred = processingErrorOccurred;
501   }
502
503   public String getNodeId() {
504     return nodeId;
505   }
506
507   public void setNodeId(String nodeId) {
508     this.nodeId = nodeId;
509   }
510
511   public String getEntityType() {
512     return entityType;
513   }
514
515   public void setEntityType(String entityType) {
516     this.entityType = entityType;
517   }
518
519   public String getSelfLink() {
520     return selfLink;
521   }
522
523   /**
524    * Calculate edit attribute uri.
525    *
526    * @param link the link
527    * @return the string
528    */
529   public String calculateEditAttributeUri(String link) {
530     String uri = null;
531     Pattern pattern = Pattern.compile(URIRegexPattern);
532     Matcher matcher = pattern.matcher(link);
533     if (matcher.find()) {
534       uri = link.substring(matcher.end());
535     }
536     return uri;
537   }
538
539   /**
540    * Analyze self link relationship list.
541    *
542    * @param jsonResult the json result
543    * @return the relationship list
544    */
545   private RelationshipList analyzeSelfLinkRelationshipList(String jsonResult) {
546
547
548     RelationshipList relationshipList = null;
549
550     try {
551       relationshipList = mapper.readValue(jsonResult, RelationshipList.class);
552     } catch (Exception exc) {
553       LOG.error(AaiUiMsgs.SELF_LINK_RELATIONSHIP_LIST_ERROR, exc.toString());
554     }
555
556     return relationshipList;
557   }
558
559   /**
560    * Adds the relationship list.
561    *
562    * @param relationshipList the relationship list
563    */
564   public void addRelationshipList(RelationshipList relationshipList) {
565
566     if (!relationshipLists.contains(relationshipList)) {
567       relationshipLists.add(relationshipList);
568     }
569
570   }
571
572   /**
573    * Process pathed self link response.
574    *
575    * @param selfLinkJsonResponse the self link json response
576    * @param startNodeType the start node type
577    * @param startNodeResourceKey the start node resource key
578    */
579   public void processPathedSelfLinkResponse(String selfLinkJsonResponse, String startNodeType,
580       String startNodeResourceKey) {
581
582     if (selfLinkJsonResponse == null || selfLinkJsonResponse.length() == 0) {
583       LOG.error(AaiUiMsgs.SELF_LINK_NULL_EMPTY_RESPONSE);
584       return;
585     }
586
587     try {
588       JsonNode jsonNode = mapper.readValue(selfLinkJsonResponse, JsonNode.class);
589
590       Iterator<Entry<String, JsonNode>> fieldNames = jsonNode.fields();
591       Entry<String, JsonNode> field = null;
592
593       while (fieldNames.hasNext()) {
594
595         field = fieldNames.next();
596
597         /*
598          * Is there a way to tell if the field is an aggregate or an atomic value? This is where our
599          * flattening code needs to live
600          */
601
602         String fieldName = field.getKey();
603
604         if ("relationship-list".equals(fieldName)) {
605
606           /*
607            * Parse the relationship list like we were doing before, so we can determine whether or
608            * not to keep it or traverse it after we have performed the evaluative node depth logic.
609            */
610           RelationshipList relationshipList =
611               analyzeSelfLinkRelationshipList(field.getValue().toString());
612
613           if (relationshipList != null) {
614             this.relationshipLists.add(relationshipList);
615           } else {
616             LOG.info(AaiUiMsgs.NO_RELATIONSHIP_DISCOVERED, nodeId);
617           }
618         } else {
619           JsonNode nodeValue = field.getValue();
620
621           if (nodeValue != null && nodeValue.isValueNode()) {
622
623             /*
624              * before we blindly add the fieldName and value to our property set, let's do one more
625              * check to see if the field name is an entity type. If it is, then our complex
626              * attribute processing code will pick it up and process it instead, but this is
627              * probably more likely just for array node types, but we'll see.
628              */
629
630             if (oxmModelLoader.getEntityDescriptor(fieldName) == null) {
631               /*
632                * this is no an entity type as far as we can tell, so we can add it to our property
633                * set.
634                */
635
636               addProperty(fieldName, nodeValue.asText());
637
638             }
639
640           } else {
641
642             if (nodeValue.isArray()) {
643
644               /*
645                * make sure array entity-type collection is not an entityType before adding it to the
646                * property set. The expetation is that it will be added the visualization through a
647                * complex group or relationship.
648                */
649
650               if (oxmModelLoader.getEntityDescriptor(field.getKey()) == null) {
651                 /*
652                  * this is no an entity type as far as we can tell, so we can add it to our property
653                  * set.
654                  */
655
656                 addProperty(field.getKey(), nodeValue.toString());
657
658               }
659
660             } else {
661
662               complexGroups.add(nodeValue);
663
664             }
665
666           }
667
668         }
669
670       }
671
672     } catch (IOException exc) {
673       LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, "POJO", exc.getLocalizedMessage());
674       this.setProcessingErrorOccurred(true);
675       this.addErrorCause(
676           "An error occurred while converting JSON into POJO = " + exc.getLocalizedMessage());
677     }
678
679   }
680
681   public void setSelfLink(String selfLink) {
682     this.selfLink = selfLink;
683   }
684
685   /**
686    * Adds the complex group.
687    *
688    * @param complexGroup the complex group
689    */
690   public void addComplexGroup(JsonNode complexGroup) {
691
692     if (!complexGroups.contains(complexGroup)) {
693       complexGroups.add(complexGroup);
694     }
695
696   }
697
698   /**
699    * Gets the padding.
700    *
701    * @param level the level
702    * @param paddingString the padding string
703    * @return the padding
704    */
705   private static String getPadding(int level, String paddingString) {
706     StringBuilder sb = new StringBuilder(32);
707     for (int x = 0; x < level; x++) {
708       sb.append(paddingString);
709     }
710     return sb.toString();
711   }
712
713   /**
714    * Dump node tree.
715    *
716    * @param showProperties the show properties
717    * @return the string
718    */
719   public String dumpNodeTree(boolean showProperties) {
720     return dumpNodeTree(0, showProperties);
721   }
722   
723   /**
724    * Dump node tree.
725    *
726    * @param level the level
727    * @param showProperties the show properties
728    * @return the string
729    */
730   private String dumpNodeTree(int level, boolean showProperties) {
731     StringBuilder sb = new StringBuilder(128);
732     String padding = getPadding(level, "   ");
733
734     sb.append(padding + " -> " + getNodeId() + "]").append("\n");
735     sb.append(padding + " -> primaryKeyName = " + primaryKeyName + "]").append("\n");
736     sb.append(padding + " -> primaryKeyValue = " + primaryKeyValue + "]").append("\n");
737     sb.append(padding + " -> entityType = " + entityType + "]").append("\n");
738
739     if (showProperties) {
740       Set<Entry<String, String>> entries = properties.entrySet();
741       for (Entry<String, String> entry : entries) {
742         sb.append(
743             padding + " ----> " + String.format("[ %s => %s ]", entry.getKey(), entry.getValue()))
744             .append("\n");
745       }
746     }
747
748     sb.append(padding + " ----> " + String.format("[ selfLink => %s ]", getSelfLink()))
749         .append("\n");
750
751     sb.append("\n").append(padding + " ----> Inbound Neighbors:").append("\n");
752
753     for (String inboundNeighbor : inboundNeighbors) {
754       sb.append("\n").append(inboundNeighbor.toString());
755     }
756
757     sb.append(padding + " ----> Outbound Neighbors:").append("\n");
758     sb.append("\n").append(padding + " ----> Outbound Neighbors:").append("\n");
759
760     for (String outboundNeighbor : outboundNeighbors) {
761       sb.append("\n").append(outboundNeighbor.toString());
762     }
763
764     return sb.toString();
765
766   }
767
768   public String getProcessingErrorCauses() {
769
770     StringBuilder sb = new StringBuilder(128);
771
772     for (String c : this.errorCauses) {
773       sb.append(c).append("\n");
774     }
775
776     return sb.toString();
777   }
778 }