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