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