DCAE-D be initial commit
[sdc/dcae-d/dt-be-main.git] / dcaedt_catalog / api / src / main / java / org / onap / sdc / dcae / catalog / asdc / ASDCCatalog.java
1 package org.onap.sdc.dcae.catalog.asdc;
2
3 import com.google.common.collect.ImmutableMap;
4 import org.apache.commons.io.IOUtils;
5 import org.apache.commons.jxpath.JXPathContext;
6 import org.apache.commons.jxpath.JXPathNotFoundException;
7 import org.apache.commons.lang3.StringUtils;
8 import org.json.JSONArray;
9 import org.json.JSONObject;
10 import org.onap.sdc.common.onaplog.Enums.LogLevel;
11 import org.onap.sdc.common.onaplog.OnapLoggerDebug;
12 import org.onap.sdc.dcae.catalog.Catalog;
13 import org.onap.sdc.dcae.catalog.commons.*;
14 import org.onap.sdc.dcae.checker.*;
15
16 import java.io.*;
17 import java.net.URI;
18 import java.net.URISyntaxException;
19 import java.util.*;
20 import java.util.function.BiFunction;
21 import java.util.stream.Collectors;
22 import java.util.stream.Stream;
23 import java.util.stream.StreamSupport;
24
25 public class ASDCCatalog implements Catalog {
26
27     private
28     static final String JXPATH_NOT_FOUND_EXCEPTION = "JXPathNotFoundException {}";
29     private
30     static final String OCCURRENCES = "occurrences";
31     private
32     static final String TOPOLOGY_TEMPLATE_NODE_TEMPLATES = "/topology_template/node_templates";
33     private
34     static final String NODES_NAME = "/nodes[name='";
35     private
36     static final String ITEM_ID = "itemId";
37     private
38     static final String LABELS = "labels";
39     private
40     static final String ARTIFACT_URL = "artifactURL";
41     private
42     static final String CAPABILITY = "capability";
43     private
44     static final String DATABASE = "Database";
45     private
46     static final String COLLECTOR = "Collector";
47     private
48     static final String MICROSERVICE = "Microservice";
49     private
50     static final String ANALYTICS = "Analytics";
51     private
52     static final String POLICY = "Policy";
53     private
54     static final String SOURCE = "Source";
55     private
56     static final String UTILITY = "Utility";
57     private
58     static final String NAME = "name";
59     private
60     static final String ID = "id";
61     private
62     static final String ARTIFACT_NAME = "artifactName";
63     private
64     static final String DESCRIPTION = "description";
65     private
66     static final String MODELS = "models";
67     private
68     static final String ARTIFACTS = "artifacts";
69     private
70     static final String ITEMS = "items";
71     private
72     static final String PROPERTIES = "']/properties";
73     private
74     static final String TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 = "/topology_template/node_templates/";
75     private
76     static final String PROPERTIES_NAME = "']/properties[name='";
77     private
78     static final String CAPABILITIES = "']/capabilities";
79     private
80     static final String CAPABILITIES_NAME = "']/capabilities[name='";
81
82     private static OnapLoggerDebug debugLogger = OnapLoggerDebug.getInstance();
83
84     private ASDC asdc;
85
86     private JSONObject folders = new JSONObject();
87     private String[] folderFields = new String[] {ID, ITEM_ID, NAME};
88
89     private ProxyBuilder proxies;
90     private Map<Target, JXPathContext> contexts = new HashMap<Target, JXPathContext>();
91
92     // resource and its catalog
93     private Map<UUID, org.onap.sdc.dcae.checker.Catalog> catalogs = new HashMap<UUID, org.onap.sdc.dcae.checker.Catalog>();
94
95     public ASDCCatalog(URI theURI) {
96
97         this.asdc = new ASDC();
98         this.asdc.setUri(theURI);
99
100         initFolders();
101
102         this.proxies = new ProxyBuilder().withConverter(v -> v == null ? null : UUID.fromString(v.toString()), UUID.class)
103                 .withExtensions(
104                 new ImmutableMap.Builder<String, BiFunction<Proxy, Object[], Object>>().put("data", (proxy, args) -> proxy.data())
105                         .build()).withContext(new ImmutableMap.Builder<String, Object>().put("catalog", this).build());
106     }
107
108     private void initFolders() {
109
110         JSONArray labels = new JSONArray();
111         labels.put("Folder");
112         labels.put("DCAE");
113         labels.put("Superportfolio"); // for CCD compatibility
114
115         folders.put(DATABASE, new JSONObject().put(NAME, DATABASE).put(ID, "dcae_database")
116                 .put(ITEM_ID, DATABASE).put(LABELS, labels));
117         folders.put(COLLECTOR, new JSONObject().put(NAME, COLLECTOR).put(ID, "dcae_collector")
118                 .put(ITEM_ID, COLLECTOR).put(LABELS, labels));
119         folders.put(MICROSERVICE, new JSONObject().put(NAME, MICROSERVICE).put(ID, "dcae_microservice")
120                 .put(ITEM_ID, MICROSERVICE).put(LABELS, labels));
121         folders.put(ANALYTICS, new JSONObject().put(NAME, ANALYTICS).put(ID, "dcae_analytics")
122                 .put(ITEM_ID, ANALYTICS).put(LABELS, labels));
123         folders.put(POLICY, new JSONObject().put(NAME, POLICY).put(ID, "dcae_policy").put(ITEM_ID, POLICY)
124                 .put(LABELS, labels));
125         folders.put(SOURCE, new JSONObject().put(NAME, SOURCE).put(ID, "dcae_source").put(ITEM_ID, SOURCE)
126                 .put(LABELS, labels));
127         folders.put(UTILITY, new JSONObject().put(NAME, UTILITY).put(ID, "dcae_utility")
128                 .put(ITEM_ID, UTILITY).put(LABELS, labels));
129     }
130
131     public URI getUri() {
132         return this.asdc.getUri();
133     }
134
135     public String namespace() {
136         return "asdc";
137     }
138
139     public boolean same(Catalog theCatalog) {
140         return true;
141     }
142
143     public <T> T proxy(JSONObject theData, Class<T> theType) {
144         return proxies.build(theData, theType);
145     }
146
147     /** */
148     public Future<Folders> roots() {
149
150         Folders roots = new Folders();
151         for (Iterator fi = folders.keys(); fi.hasNext();) {
152             roots.add(proxies.build(folders.getJSONObject((String) fi.next()), Folder.class));
153         }
154         return Futures.succeededFuture(roots);
155     }
156
157     /** */
158     public Future<Folders> rootsByLabel(String theLabel) {
159
160         Folders roots = new Folders();
161         for (Iterator fi = folders.keys(); fi.hasNext();) {
162             JSONObject folder = folders.getJSONObject((String) fi.next());
163             JSONArray labels = folder.getJSONArray(LABELS);
164
165             for (int i = 0; i < labels.length(); i++) {
166                 if (labels.get(i).equals(theLabel)) {
167                     roots.add(proxies.build(folder, Folder.class));
168                 }
169             }
170         }
171         return Futures.succeededFuture(roots);
172     }
173
174     /** */
175     public Future<Mixels> lookup(JSONObject theSelector) {
176         return Futures.succeededFuture(new Mixels());
177     }
178
179     public Future<Mixels> lookup(String theAnnotation, JSONObject theSelector) {
180         return Futures.succeededFuture(new Mixels());
181     }
182
183     /** */
184     public ItemAction item(String theItemId) {
185         return new ResourceAction(UUID.fromString(theItemId));
186     }
187
188     /** */
189     public FolderAction folder(String theFolderId) {
190         return new FolderAction(theFolderId);
191     }
192
193     public TemplateAction template(String theId) {
194         return new TemplateAction(theId);
195     }
196
197     public TypeAction type(String theItemId, String theName) {
198         return new TypeAction(UUID.fromString(theItemId), theName);
199     }
200
201     protected static String resolveTargetName(Target theTarget) {
202         return (String) ((Map) ((Map) theTarget.getTarget()).get("metadata")).get("template_name");
203     }
204
205     protected Object resolve(Target theTarget, String thePath) {
206         try {
207             return contexts.get(theTarget).getValue(thePath);
208         } catch (JXPathNotFoundException pnfx) {
209             debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "JXPathNotFoundException {}", pnfx);
210             return null;
211         }
212     }
213
214     // covers common TOSCA pattern of single entry maps
215     public Map.Entry<String, Map> toEntry(Object theValue) {
216         return (Map.Entry<String, Map>) ((Map) theValue).entrySet().iterator().next();
217     }
218
219     protected Map selectEntries(Map theOriginal, String... theKeys) {
220         Arrays.sort(theKeys);
221         Map selection = ((Set<Map.Entry>) theOriginal.entrySet()).stream()
222                 .filter(e -> Arrays.binarySearch(theKeys, e.getKey().toString()) >= 0)
223                 .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
224         return selection;
225     }
226
227     protected Map evictEntries(Map theOriginal, String... theKeys) {
228         Arrays.sort(theKeys);
229         Map selection = ((Set<Map.Entry>) theOriginal.entrySet()).stream()
230                 .filter(e -> Arrays.binarySearch(theKeys, e.getKey().toString()) < 0)
231                 .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
232         return selection;
233     }
234
235     protected MapBuilder renderEntry(Map.Entry theEntry, String... theKeys) {
236         MapBuilder out = new MapBuilder();
237         out.put(NAME, theEntry.getKey());
238
239         for (String key : theKeys) {
240             out.put(key, ((Map) theEntry.getValue()).get(key));
241         }
242         return out;
243     }
244
245     protected <T> Stream<T> stream(Iterator<T> theSource) {
246         return StreamSupport.stream(Spliterators.spliteratorUnknownSize(theSource,
247                 Spliterator.NONNULL | Spliterator.DISTINCT | Spliterator.IMMUTABLE), false);
248     }
249
250     private JSONArray selectModels(JSONArray theArtifacts) {
251         JSONArray models = new JSONArray();
252         if (theArtifacts == null) {
253             return models;
254         }
255
256         for (int i = 0; i < theArtifacts.length(); i++) {
257             JSONObject artifact = theArtifacts.getJSONObject(i);
258             String name = artifact.optString(ARTIFACT_NAME);
259             if (name != null && StringUtils.containsIgnoreCase(name, "template")) {
260                 models.put(new JSONObject().putOpt(NAME, artifact.optString(ARTIFACT_NAME))
261                         .putOpt("version", artifact.optString("artifactVersion"))
262                         .putOpt(DESCRIPTION, artifact.optString("artifactType"))
263                         .putOpt(ID, artifact.optString(ARTIFACT_URL))
264                         .putOpt(ITEM_ID, artifact.optString(ARTIFACT_URL)));
265             }
266         }
267         return models;
268     }
269
270     private JSONObject patchResource(JSONObject theResource) {
271
272         theResource.remove("resources");
273         theResource.putOpt(ID, theResource.opt("uuid"));
274         theResource.putOpt(ITEM_ID, theResource.opt("uuid"));
275
276         return theResource;
277     }
278
279     private static void dumpTargets(String theDirName, Collection<Target> theTargets) {
280         try {
281             File targetDir = new File(theDirName);
282             if (!targetDir.exists() && !targetDir.mkdirs()) {
283                 throw new IllegalStateException("Couldn't create dir: " + theDirName);
284             }
285             for (Target t : theTargets) {
286                 FileWriter dump = new FileWriter(new File(theDirName, t.getName()));
287                 IOUtils.copy(t.open(), dump);
288                 dump.close();
289             }
290         } catch (IOException iox) {
291             debugLogger.log(LogLevel.DEBUG,"ASDCCatalog", "IOException {}", iox);
292         }
293     }
294
295     private static URI asURI(String theValue) {
296         try {
297             return new URI(theValue);
298         } catch (URISyntaxException urisx) {
299             throw new IllegalArgumentException("Invalid URI", urisx);
300         }
301     }
302
303     private static UUID asUUID(String theValue) {
304         return UUID.fromString(theValue);
305     }
306
307     private org.onap.sdc.dcae.checker.Catalog getCatalog(UUID theResourceId) {
308         return this.catalogs.get(theResourceId);
309     }
310
311     private String getArtifactVersion(JSONObject theData) {
312         return theData.getString("artifactVersion");
313     }
314
315     private String getArtifactName(JSONObject theData) {
316         return theData.getString(ARTIFACT_NAME);
317     }
318
319     private String getArtifactURL(JSONObject theData) {
320         return theData.getString(ARTIFACT_URL);
321     }
322
323     private URI getArtifactURI(JSONObject theData) {
324         return asURI(theData.getString(ARTIFACT_URL));
325     }
326
327     /** */
328     public class ResourceAction implements Catalog.ItemAction<Resource> {
329
330         private UUID iid;
331         private boolean doModels;
332
333         ResourceAction(UUID theItemId) {
334             this.iid = theItemId;
335         }
336
337         public ResourceAction withModels() {
338             this.doModels = true;
339             return this;
340         }
341
342         public ResourceAction withAnnotations() {
343             return this;
344         }
345
346         @Override
347         public Future<Resource> execute() {
348
349             return Futures.advance(asdc.getResource(this.iid, JSONObject.class), resourceData -> {
350                 if (doModels) {
351                     resourceData.put(MODELS, selectModels(resourceData.optJSONArray(ARTIFACTS)));
352                 }
353                 return proxies.build(patchResource(resourceData), Resource.class);
354             });
355         }
356
357         protected Future<JSONObject> executeRaw() {
358
359             return Futures.advance(asdc.getResource(this.iid, JSONObject.class), resourceData -> {
360                 if (doModels) {
361                     resourceData.put(MODELS, selectModels(resourceData.optJSONArray(ARTIFACTS)));
362                 }
363                 return resourceData;
364             }, resourceError -> new RuntimeException("Failed to retrieve item " + this.iid, resourceError));
365         }
366     }
367
368     public class FolderAction implements Catalog.FolderAction {
369
370         private boolean doItemModels;
371         private String folderName;
372
373         // use the id/UUID of the folder ??
374         private FolderAction(String theFolderName) {
375             this.folderName = theFolderName;
376         }
377
378         public FolderAction withAnnotations() {
379             return this;
380         }
381
382         public FolderAction withAnnotations(String theSelector) {
383             return this;
384         }
385
386         public FolderAction withItems() {
387             return this;
388         }
389
390         public FolderAction withItemAnnotations() {
391             return this;
392         }
393
394         public FolderAction withItemAnnotations(String theSelector) {
395             return this;
396         }
397
398         public FolderAction withItemModels() {
399             doItemModels = true;
400             return this;
401         }
402
403         public FolderAction withParts() {
404             return this;
405         }
406
407         public FolderAction withPartAnnotations() {
408             return this;
409         }
410
411         public FolderAction withPartAnnotations(String theSelector) {
412             return this;
413         }
414
415         @Override
416         public Future<Folder> execute() {
417
418             JSONObject folder = folders.optJSONObject(this.folderName);
419             if (folder == null) {
420                 return Futures.failedFuture(new RuntimeException("No such folder " + this.folderName));
421             }
422
423             final JSONObject folderView = new JSONObject(folder, folderFields);
424
425             return Futures.advance(asdc.getResources(JSONArray.class, "DCAE Component", this.folderName),
426                     resourcesData -> {
427
428                         Actions.CompoundAction<Resource> itemsAction = new Actions.BasicCompoundAction<Resource>();
429                         for (int i = 0; i < resourcesData.length(); i++) {
430                             JSONObject resource = resourcesData.getJSONObject(i);
431
432                             if (doItemModels) {
433                                 itemsAction
434                                         .addAction(new ResourceAction(asUUID(resource.getString("uuid"))).withModels());
435                             } else {
436                                 folderView.append(ITEMS, patchResource(resource));
437                             }
438                         }
439
440                         try {
441                             List<Resource> items = itemsAction.execute().waitForResult();
442                             debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Number of DCAE item for : {} is {}", this.folderName, items.size());
443
444                             for (Resource res : filterLatestVersion(items)) {
445                                 folderView.append(ITEMS, patchResource(res.data()));
446                             }
447                         } catch (Exception x) {
448                             debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Exception {}", x);
449                             throw new RuntimeException("Failed to retrieve folder items", x);
450                         }
451
452                         return proxies.build(folderView, Folder.class);
453                     }, resourcesError -> new RuntimeException("Failed to retrieve resources", resourcesError));
454         }
455
456         public Collection<Resource> filterLatestVersion(Collection<Resource> items) throws IllegalArgumentException {
457             if (items == null) {
458                 throw new IllegalArgumentException("null is not acceptable as a list of items");
459             }
460             Map<UUID, Resource> itemsMap = new HashMap<UUID, Resource>(items.size());
461             for (Resource r : items) {
462                 if (itemsMap.containsKey(r.invariantUUID()) && isNewerVersion(itemsMap, r)) {
463                     debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Avoiding adding item {} since it has a advanced version already", r.toString());
464                     continue;
465                 }
466                 itemsMap.put(r.invariantUUID(), r);
467             }
468             return itemsMap.values();
469         }
470
471         private boolean isNewerVersion(Map<UUID, Resource> itemsMap, Resource r) {
472             return Float.valueOf(itemsMap.get(r.invariantUUID()).version()) > Float.valueOf(r.version());
473         }
474
475     }
476
477     /** */
478     public class TemplateAction implements Catalog.TemplateAction {
479
480         private String artifactId;
481         private Target target;
482         private org.onap.sdc.dcae.checker.Catalog catalog;
483         private JXPathContext ctx = JXPathContext.newContext(new HashMap());
484
485         private boolean doNodes, doNodeProperties, doNodePropertiesAssignments, doNodeRequirements, doNodeCapabilities,
486                 doNodeCapabilityProperties, doNodeCapabilityPropertyAssignments;
487
488         protected TemplateAction(Target theTarget) {
489             this.target = theTarget;
490         }
491
492         /*
493          * expected to be the relative url provided by asdc for the template
494          * artifact
495          */
496         protected TemplateAction(String theArtifactId) {
497             this.artifactId = theArtifactId;
498         }
499
500         public TemplateAction withInputs() {
501             return this;
502         }
503
504         public TemplateAction withOutputs() {
505             return this;
506         }
507
508         public TemplateAction withNodes() {
509             this.doNodes = true;
510             return this;
511         }
512
513         protected TemplateAction doNodes() {
514             if (!this.doNodes) {
515                 return this;
516             }
517
518             Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
519             if (nodes == null) {
520                 return this;
521             }
522
523             ctx.setValue("/nodes",
524                     nodes.entrySet().stream()
525                             .map(nodeEntry -> new MapBuilder().put(NAME, ((Map.Entry) nodeEntry).getKey())
526                                     .put(DESCRIPTION, this.artifactId)
527                                     .putAll(selectEntries((Map) ((Map.Entry) nodeEntry).getValue(), "type")).build())
528                             .collect(Collectors.toList()));
529
530             return this;
531         }
532
533         // pre-requisite: a call to 'withNodes'
534         public TemplateAction withNodeProperties() {
535             this.doNodeProperties = true;
536             return this;
537         }
538
539         protected TemplateAction doNodeProperties() {
540             if (!this.doNodeProperties) {
541                 return this;
542             }
543
544             Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
545             if (nodes == null) {
546                 return this;
547             }
548
549             nodes.entrySet().stream().forEach(node -> ctx.setValue(
550                     NODES_NAME + ((Map.Entry) node).getKey() + PROPERTIES,
551                     stream(catalog.facets(Construct.Node, Facet.properties,
552                             ((Map) ((Map.Entry) node).getValue()).get("type").toString()))
553                                     .map(propEntry -> new MapBuilder().put(NAME, propEntry.getKey())
554                                             .putAll((Map) propEntry.getValue()).build())
555                                     .collect(Collectors.toList())));
556
557             return this;
558         }
559
560         // pre-requisite: a call to 'withNodesProperties'
561         public TemplateAction withNodePropertiesAssignments() {
562             this.doNodePropertiesAssignments = true;
563             return this;
564         }
565
566         protected TemplateAction doNodePropertiesAssignments() {
567             if (!this.doNodePropertiesAssignments) {
568                 return this;
569             }
570
571             Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
572             if (nodes == null) {
573                 return this;
574             }
575
576             nodes.entrySet().stream().forEach(node -> {
577                 List nodeProps = null;
578                 try {
579                     nodeProps = (List) ctx.getValue(NODES_NAME + ((Map.Entry) node).getKey() + PROPERTIES);
580                 } catch (JXPathNotFoundException pnfx) {
581                     debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx);
582                     return;
583                 }
584
585                 nodeProps.stream().forEach(prop -> {
586                     // pick from
587                     String propPath = TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 + ((Map.Entry) node).getKey()
588                             + "/properties/" + ((Map) prop).get(NAME);
589                     Object propValue = resolve(this.target, propPath);
590                     // to conform with the db based api we should analyze the
591                     // value for function calls
592                     // dump at ..
593                     propPath = NODES_NAME + ((Map.Entry) node).getKey() + PROPERTIES_NAME
594                             + ((Map) prop).get(NAME) + "']";
595                     if (propValue != null) {
596                         ctx.setValue(propPath + "/assignment",
597                                 new ImmutableMap.Builder().put("value", propValue).build());
598                     }
599                 });
600             });
601
602             return this;
603         }
604
605         protected Map renderRequirementDefinition(Map.Entry theReq) {
606             Map def = (Map) theReq.getValue();
607             return new MapBuilder().put(NAME, theReq.getKey())
608                     // capability must be present
609                     .put(CAPABILITY,
610                             new MapBuilder().put(NAME, def.get(CAPABILITY))
611                                     .put(ID, this.target.getName() + "/" + def.get(CAPABILITY)).build())
612                     .putAll(evictEntries(def, CAPABILITY)).build();
613         }
614
615         // TODO: see how this comes out of neo and match it
616         protected Map renderRequirementAssignment(Map.Entry theReq) {
617             Map def = (Map) theReq.getValue();
618             return new MapBuilder().put(NAME, theReq.getKey())
619                     // capability must be present
620                     .put(CAPABILITY,
621                             new MapBuilder().put(NAME, def.get(CAPABILITY))
622                                     // we provide an id only if the capability
623                                     // points to a type
624                                     .putOpt(ID,
625                                             catalog.hasType(Construct.Capability, (String) def.get(CAPABILITY))
626                                                     ? (this.target.getName() + "/" + def.get(CAPABILITY)) : null)
627                                     .build())
628                     .putAll(evictEntries(def, CAPABILITY)).build();
629         }
630
631         public TemplateAction withNodeRequirements() {
632             this.doNodeRequirements = true;
633             return this;
634         }
635
636         TemplateAction doNodeRequirements() {
637             if (!this.doNodeRequirements) {
638                 return this;
639             }
640
641             // requirements come first from the type and then can be further
642             // refined by their assignment within the
643             // node template
644             Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
645             if (nodes == null) {
646                 return this;
647             }
648
649             // type
650             nodes.entrySet().stream()
651                     .forEach(
652                             node -> ctx
653                                     .setValue(
654                                             NODES_NAME
655                                                     + ((Map.Entry) node)
656                                                             .getKey()
657                                                     + "']/requirements",
658                                             StreamSupport
659                                                     .stream(Spliterators.spliteratorUnknownSize(
660                                                             catalog.requirements(((Map) ((Map.Entry) node).getValue())
661                                                                     .get("type").toString()),
662                                                             Spliterator.NONNULL | Spliterator.DISTINCT
663                                                                     | Spliterator.IMMUTABLE),
664                                                             false)
665                                                     .map((Map.Entry reqEntry) -> renderRequirementDefinition(reqEntry))
666                                                     .collect(Collectors.toList())));
667
668             // merge assignments on top of definitions
669             nodes.entrySet().stream().forEach(node -> {
670                 List nodeReqsAssigns = (List) resolve(this.target,
671                         TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 + ((Map.Entry) node).getKey() + "/requirements");
672                 if (nodeReqsAssigns == null) {
673                     return;
674                 }
675                 nodeReqsAssigns.stream().forEach(req -> {
676                     Map.Entry reqAssign = toEntry(req);
677                     catalog.mergeDefinitions((Map) ctx.getValue(NODES_NAME + ((Map.Entry) node).getKey()
678                             + "']/requirements[name='" + reqAssign.getKey() + "']"),
679                             renderRequirementAssignment(reqAssign));
680                 });
681             });
682
683             return this;
684         }
685
686         public TemplateAction withNodeCapabilities() {
687             this.doNodeCapabilities = true;
688             return this;
689         }
690
691         protected Map renderCapabilityDefinition(Map.Entry theCap) {
692             Map def = (Map) theCap.getValue();
693             return new MapBuilder().put(NAME, theCap.getKey())
694                     .put("type",
695                             new MapBuilder().put(NAME, def.get("type"))
696                                     .put(ID, this.target.getName() + "/" + def.get("type")).build())
697                     .putAll(evictEntries(def, "properties", "type")).build();
698         }
699
700         TemplateAction doNodeCapabilities() {
701             if (!this.doNodeCapabilities) {
702                 return this;
703             }
704
705             Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
706             if (nodes == null) {
707                 return this;
708             }
709
710             // collect capabilities through the node type hierarchy
711
712             // we evict the properties from the node type capability declaration
713             // (when declaring a capability with the
714             // node type some re-definition of capability properties can take
715             // place).
716             nodes.entrySet().stream()
717                     .forEach(node -> ctx.setValue(NODES_NAME + ((Map.Entry) node).getKey() + CAPABILITIES,
718
719                             stream(catalog.facets(Construct.Node, Facet.capabilities,
720                                     ((Map) ((Map.Entry) node).getValue()).get("type").toString()))
721                                             .map((Map.Entry capEntry) -> renderCapabilityDefinition(capEntry))
722                                             .collect(Collectors.toList())));
723
724             return this;
725         }
726
727         public TemplateAction withNodeCapabilityProperties() {
728             this.doNodeCapabilityProperties = true;
729             return this;
730         }
731
732         TemplateAction doNodeCapabilityProperties() {
733
734             if (!this.doNodeCapabilityProperties) {
735                 return this;
736             }
737
738             Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES);
739             if (nodes == null) {
740                 return this;
741             }
742
743             // pick up all the properties from the capability type hierarchy
744             // definition
745             nodes.entrySet().stream().forEach(node -> {
746                 List nodeCapabilities = (List) ctx
747                         .getValue(NODES_NAME + ((Map.Entry) node).getKey() + CAPABILITIES);
748                 if (nodeCapabilities == null) {
749                     return;
750                 }
751
752                 // collect properties from the capability type hierarchy
753                 nodeCapabilities.stream().forEach(capability -> {
754                     List capabilityProperties = StreamSupport
755                             .stream(Spliterators.spliteratorUnknownSize(
756                                     catalog.facets(Construct.Capability, Facet.properties,
757                                             ((Map)((Map)capability).get("type")).get(NAME).toString()),
758                                     Spliterator.NONNULL | Spliterator.DISTINCT | Spliterator.IMMUTABLE), false)
759                             .map((Map.Entry capEntry) -> new MapBuilder().put(NAME, capEntry.getKey())
760                                     .putAll((Map) capEntry.getValue()).build())
761                             .collect(Collectors.toList());
762
763                     if (!capabilityProperties.isEmpty()) {
764                         ctx.setValue(NODES_NAME + ((Map.Entry) node).getKey() + CAPABILITIES_NAME
765                                 + ((Map) capability).get(NAME) + PROPERTIES, capabilityProperties);
766                     }
767                 });
768
769                 // and go over the node type (hierarchy) and pick up any
770                 // re-definitions from there.
771                 StreamSupport
772                         .stream(Spliterators.spliteratorUnknownSize(
773                                 catalog.facets(Construct.Node, Facet.capabilities,
774                                         ((Map) ((Map.Entry) node).getValue()).get("type").toString()),
775                                 Spliterator.NONNULL | Spliterator.DISTINCT | Spliterator.IMMUTABLE), false)
776                         .forEach((Map.Entry capability) -> {
777                             // for each capability property that has some node
778                             // type level re-definition
779                             Map properties = (Map) ((Map) capability.getValue()).get("properties");
780                             if (properties == null) {
781                                 return;
782                             }
783
784                             properties.entrySet().stream().forEach(property -> {
785                                 String propertyLoc = NODES_NAME + ((Map.Entry) node).getKey()
786                                         + CAPABILITIES_NAME + ((Map) capability).get(NAME)
787                                         + PROPERTIES_NAME + ((Map.Entry) property).getKey() + "']";
788                                 ctx.setValue(propertyLoc, catalog.mergeDefinitions((Map) ctx.getValue(propertyLoc),
789                                         (Map) ((Map.Entry) property).getValue()));
790                             });
791                         });
792             });
793
794             return this;
795         }
796
797         public TemplateAction withNodeCapabilityPropertyAssignments() {
798             this.doNodeCapabilityPropertyAssignments = true;
799             return this;
800         }
801
802         TemplateAction doNodeCapabilityPropertyAssignments() {
803             if (!this.doNodeCapabilityPropertyAssignments) {
804                 return this;
805             }
806
807             // this is a wasteful: we go over all declared
808             // nodes/capabilities/properties and check if there is an assigned
809             // value in the actual template. It is optimal to approach the
810             // problem from the other direction: go over delared
811             // assignments and set them in the output structure ..
812
813             List nodes = null;
814             try {
815                 nodes = (List) ctx.getValue("/nodes");
816             } catch (JXPathNotFoundException pnfx) {
817                 debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx);
818                 return this;
819             }
820
821             nodes.stream().forEach(node -> {
822                 List capabilities = (List) ctx.getValue(NODES_NAME + ((Map) node).get(NAME) + CAPABILITIES);
823                 if (capabilities == null) {
824                     return;
825                 }
826
827                 capabilities.stream().forEach(capability -> {
828                     List properties = null;
829                     try {
830                         properties = (List) ctx.getValue(NODES_NAME + ((Map) node).get(NAME)
831                                 + CAPABILITIES_NAME + ((Map) capability).get(NAME) + PROPERTIES);
832                     } catch (JXPathNotFoundException pnfx) {
833                         debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx);
834                         return;
835                     }
836
837                     properties.stream().forEach(property -> {
838                         String location = NODES_NAME + ((Map) node).get(NAME) + CAPABILITIES_NAME
839                                 + ((Map) capability).get(NAME) + PROPERTIES_NAME + ((Map) property).get(NAME)
840                                 + "']/assignment";
841
842                         // pick the value from the original
843                         try {
844                             Object assignment = resolve(this.target,
845                                     TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 + ((Map) node).get(NAME) + "/capabilities/"
846                                             + ((Map) capability).get(NAME) + "/properties/"
847                                             + ((Map) property).get(NAME));
848                             if (assignment != null) {
849                                 ctx.setValue(location, new ImmutableMap.Builder().put("value", assignment).build());
850                             }
851                         } catch (JXPathNotFoundException pnfx) {
852                             debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx);
853                             // it's ok, no assignment
854                         }
855                     });
856                 });
857             });
858
859             return this;
860         }
861
862         public TemplateAction withPolicies() {
863             return this;
864         }
865
866         public TemplateAction withPolicyProperties() {
867             return this;
868         }
869
870         public TemplateAction withPolicyPropertiesAssignments() {
871             return this;
872         }
873
874         public Future<Template> execute() {
875
876             if (this.target == null) {
877
878                 String[] parts = this.artifactId.split("/");
879                 if (parts.length != 8) {
880                     return Futures
881                             .failedFuture(new Exception("Unexpected artifact id for template " + this.artifactId));
882                 }
883
884                 UUID resourceId = asUUID(parts[5]);
885                 this.catalog = ASDCCatalog.this.catalogs.get(resourceId);
886
887                 // if we find a catalog for this resource we have to figure out
888                 // if it contains the required target ..
889
890                 try {
891                     JSONObject resource = new ResourceAction(resourceId).executeRaw().waitForResult();
892
893                     Checker checker = new Checker();
894                     TargetLocator locator = new ASDCLocator(resource.getJSONArray(ARTIFACTS),
895                             ASDCCatalog.this.catalogs.get(resourceId));
896                     checker.setTargetLocator(locator);
897
898                     Target template = locator.resolve("template");
899                     if (template == null) {
900                         return Futures.failedFuture(new Exception("Failed to locate template in " + resource));
901                     }
902
903                     checker.check(template);
904
905                     for (Target t : checker.targets()) {
906                         if (t.getReport().hasErrors()) {
907                             dumpTargets(resourceId.toString(), checker.targets());
908                             return Futures.failedFuture(new Exception("Failed template validation: " + t.getReport()));
909                         }
910                     }
911
912                     this.target = template;
913                     this.catalog = checker.catalog();
914                     ASDCCatalog.this.catalogs.put(resourceId, this.catalog);
915                     // we should only be doing this if we discovered an update
916                     // (by checking timestampts). Actually, we should
917                     // only do the artifact fetching if we detect an update
918                     ASDCCatalog.this.contexts.put(template, JXPathContext.newContext(template.getTarget()));
919                 } catch (Exception x) {
920                     return Futures.failedFuture(x);
921                 }
922             }
923
924             this.doNodes().doNodeProperties().doNodePropertiesAssignments().doNodeRequirements().doNodeCapabilities()
925                     .doNodeCapabilityProperties().doNodeCapabilityPropertyAssignments();
926
927             JSONObject pack = new JSONObject((Map) ctx.getContextBean()).put(NAME, this.target.getName().toString())
928                     .put(ID, this.target.getLocation().toString())
929                     .put(ITEM_ID, this.target.getLocation().toString());
930             debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), pack.toString(2));
931
932             return Futures.succeededFuture(proxies.build(pack, Template.class));
933         }
934     }
935
936     public class TypeAction implements Catalog.TypeAction {
937
938         private String name;
939         private UUID resourceId;
940         private JXPathContext ctx;
941
942         private boolean doHierarchy = false, doRequirements = false, doCapabilities = false;
943
944         private TypeAction(UUID theResourceId, /* Construct theConstruct, */ String theName) {
945             this.resourceId = theResourceId;
946             this.name = theName;
947         }
948
949         public TypeAction withHierarchy() {
950             this.doHierarchy = true;
951             return this;
952         }
953
954         TypeAction doHierarchy(org.onap.sdc.dcae.checker.Catalog theCatalog) {
955             if (!this.doHierarchy) {
956                 return this;
957             }
958
959             ctx.setValue("/hierarchy",
960                     stream(theCatalog.hierarchy(Construct.Node, this.name)).skip(1) // skip
961                                                                                     // self
962                             .map((Map.Entry type) -> new MapBuilder()
963                                     .put(NAME, type.getKey()).put(ID, resourceId + "/" + type.getKey())
964                                     .putOpt(DESCRIPTION, ((Map) type.getValue()).get(DESCRIPTION)).build())
965                             // renderEntry((Map.Entry)type,
966                             // "description").build())
967                             .collect(Collectors.toList()));
968             return this;
969         }
970
971         public TypeAction withRequirements() {
972             this.doRequirements = true;
973             return this;
974         }
975
976         TypeAction doRequirements(org.onap.sdc.dcae.checker.Catalog theCatalog) {
977             if (!this.doRequirements) {
978                 return this;
979             }
980
981             ctx.setValue("requirements", stream(theCatalog.requirements(this.name)).map((Map.Entry req) -> {
982                 String capability = (String) ((Map) req.getValue()).get(CAPABILITY),
983                         node = (String) ((Map) req.getValue()).get(CAPABILITY);
984                 return new MapBuilder().put(NAME, req.getKey()).put(ID, resourceId + "/" + req.getKey())
985                         .put(OCCURRENCES, ((Map) req.getValue()).get(OCCURRENCES))
986                         .put(CAPABILITY,
987                                 new MapBuilder().put(NAME, capability)
988                                         // if the capability points to a
989                                         // capability type then encode
990                                         // the type reference, else it is a name
991                                         // (within a node type)
992                                         .put(ID,
993                                                 getCatalog(resourceId).hasType(Construct.Capability, capability)
994                                                         ? (resourceId + "/" + capability) : capability.toString())
995                                         .build())
996                         .put("node", new MapBuilder().putOpt(NAME, node).putOpt(ID, node == null ? null
997                                 : (resourceId + "/" + node)).buildOpt())
998                         .put("relationship", ((Map) req.getValue()).get("relationship"))
999                         // renderEntry((Map.Entry)requirement, "occurrences",
1000                         // "node", "capability", "relationship")
1001                         .build();
1002             }).collect(Collectors.toList()));
1003
1004             return this;
1005         }
1006
1007         public TypeAction withCapabilities() {
1008             this.doCapabilities = true;
1009             return this;
1010         }
1011
1012         TypeAction doCapabilities(org.onap.sdc.dcae.checker.Catalog theCatalog) {
1013             if (!this.doCapabilities) {
1014                 return this;
1015             }
1016
1017             ctx.setValue("capabilities",
1018                     stream(theCatalog
1019                             .facets(Construct.Node, Facet.capabilities,
1020                                     this.name))
1021                                             .map((Map.Entry capability) -> new MapBuilder()
1022                                                     .put(NAME, capability.getKey()).put("type",
1023                                                             new MapBuilder()
1024                                                                     .put(NAME, ((Map) capability.getValue())
1025                                                                             .get("type"))
1026                                                                     .put(ID,
1027                                                                             resourceId + "/"
1028                                                                                     + ((Map) capability.getValue())
1029                                                                                             .get("type"))
1030                                                                     .build())
1031                                                     .put(OCCURRENCES,
1032                                                             ((Map) capability.getValue()).get(OCCURRENCES))
1033                                                     .putOpt("validSourceTypes",
1034                                                             ((Map) capability.getValue()).get("validSourceTypes"))
1035                                                     .build()
1036                                             // renderEntry((Map.Entry)capability,
1037                                             // "occurrences",
1038                                             // "validSourceTypes")
1039                                             ).collect(Collectors.toList()));
1040             return this;
1041         }
1042
1043         public Future<Type> execute() {
1044             org.onap.sdc.dcae.checker.Catalog catalog = ASDCCatalog.this.catalogs.get(this.resourceId);
1045             if (catalog == null) {
1046                 return Futures.failedFuture(new Exception("No catalog available for resource " + this.resourceId
1047                         + ". You might want to fetch the model first."));
1048             }
1049
1050             if (!catalog.hasType(Construct.Node, this.name)) {
1051                 return Futures.failedFuture(
1052                         new Exception("No " + this.name + " type in catalog for resource " + this.resourceId));
1053             }
1054
1055             this.ctx = JXPathContext
1056                     .newContext(new MapBuilder().put(NAME, this.name).put(ID, this.resourceId + "/" + this.name)
1057                             .put(ITEM_ID, this.resourceId + "/" + this.name).build());
1058
1059             this.doHierarchy(catalog).doRequirements(catalog).doCapabilities(catalog);
1060
1061             JSONObject pack = new JSONObject((Map) this.ctx.getContextBean());
1062             debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), pack.toString(2));
1063
1064             return Futures.succeededFuture(proxies.build((Map) ctx.getContextBean(), Type.class));
1065         }
1066     }
1067
1068     public static interface Resource extends Catalog.Item<Resource> {
1069
1070         @Override
1071         @Proxy.DataMap(map = "uuid")
1072         public String id();
1073
1074         public UUID uuid();
1075
1076         public UUID invariantUUID();
1077
1078         public String category();
1079
1080         public String subCategory();
1081
1082         public String lastUpdaterFullName();
1083
1084         public String version();
1085
1086         @Proxy.DataMap(proxy = true, elementType = Artifact.class)
1087         public Artifacts artifacts();
1088
1089     }
1090
1091     public static class Resources extends Elements<Resource> {
1092     }
1093
1094     public static interface Artifact extends Catalog.Element<Artifact> {
1095
1096         @Proxy.DataMap(map = ARTIFACT_NAME)
1097         public String name();
1098
1099         @Proxy.DataMap(map = "artifactType")
1100         public String type();
1101
1102         @Proxy.DataMap(map = "artifactDescription")
1103         public String description();
1104
1105         @Proxy.DataMap(map = "artifactUUID")
1106         public UUID uuid();
1107
1108         @Proxy.DataMap(map = "artifactVersion")
1109         public int version();
1110
1111     }
1112
1113     public static class Artifacts extends Elements<Artifact> {
1114     }
1115
1116     public class ASDCLocator implements TargetLocator {
1117
1118         private JSONArray artifacts;
1119         private org.onap.sdc.dcae.checker.Catalog catalog;
1120
1121         private ASDCLocator(JSONArray theArtifacts, org.onap.sdc.dcae.checker.Catalog theCatalog) {
1122             this.artifacts = theArtifacts;
1123             this.catalog = theCatalog;
1124         }
1125
1126         public boolean addSearchPath(URI theURI) {
1127             return false;
1128         }
1129
1130         public boolean addSearchPath(String thePath) {
1131             return false;
1132         }
1133
1134         public Iterable<URI> searchPaths() {
1135             return Collections.emptySet();
1136         }
1137
1138         public Target resolve(String theName) {
1139             JSONObject targetArtifact = null;
1140
1141             for (int i = 0; i < this.artifacts.length(); i++) {
1142                 JSONObject artifact = this.artifacts.getJSONObject(i);
1143                 String artifactName = artifact.getString(ARTIFACT_NAME);
1144                 if (StringUtils.containsIgnoreCase(artifactName, theName)) {
1145                     targetArtifact = artifact;
1146                 }
1147             }
1148
1149             if (targetArtifact == null) {
1150                 return null;
1151             }
1152
1153             ASDCTarget target = null;
1154             if (this.catalog != null) {
1155                 // this is the caching!!
1156                 target = (ASDCTarget) this.catalog.getTarget(ASDCCatalog.this.getArtifactURI(targetArtifact));
1157                 if (target != null && target.getVersion().equals(ASDCCatalog.this.getArtifactVersion(targetArtifact))) {
1158                     return target;
1159                 }
1160             }
1161
1162             return new ASDCTarget(targetArtifact);
1163         }
1164     }
1165
1166     public class ASDCTarget extends Target {
1167
1168         private String content;
1169         private JSONObject artifact;
1170
1171         private ASDCTarget(JSONObject theArtifact) {
1172             super(ASDCCatalog.this.getArtifactName(theArtifact), ASDCCatalog.this.getArtifactURI(theArtifact));
1173             this.artifact = theArtifact;
1174         }
1175
1176         // here is a chance for caching within the catalog! Do not go fetch the
1177         // artifact if it has not been changed since the
1178         // last fetch.
1179
1180         @Override
1181         public Reader open() throws IOException {
1182             if (this.content == null) {
1183                 try {
1184                     this.content = ASDCCatalog.this.asdc
1185                             .fetch(ASDCCatalog.this.getArtifactURL(this.artifact), String.class).waitForResult();
1186                 } catch (Exception x) {
1187                     throw new IOException("Failed to load " + ASDCCatalog.this.getArtifactURL(this.artifact), x);
1188                 }
1189             }
1190
1191             // should return immediately a reader blocked until content
1192             // available .. hard to handle errors
1193             return new StringReader(this.content);
1194         }
1195
1196         public String getVersion() {
1197             return ASDCCatalog.this.getArtifactVersion(this.artifact);
1198         }
1199
1200     }
1201
1202     public static void main(String[] theArgs) throws Exception {
1203
1204         ASDCCatalog catalog = new ASDCCatalog(new URI(theArgs[0]));
1205
1206         Folder f = catalog.folder(theArgs[1]).withItems().withItemModels().execute().waitForResult();
1207
1208         debugLogger.log(LogLevel.DEBUG, ASDCCatalog.class.getName(), "folder: {}", f.data());
1209
1210         Resources items = f.elements(ITEMS, Resources.class);
1211         if (items != null) {
1212             for (Resource item : items) {
1213                 debugLogger.log(LogLevel.DEBUG, ASDCCatalog.class.getName(), "\titem: {} : {}",item.name(), item.data());
1214                 Templates templates = item.elements(MODELS, Templates.class);
1215                 if (templates != null) {
1216                     for (Template t : templates) {
1217                         Template ft = catalog.template(t.id()).withNodes().withNodeProperties()
1218                                 .withNodePropertiesAssignments().execute().waitForResult();
1219
1220                         debugLogger.log(LogLevel.DEBUG, ASDCCatalog.class.getName(), "template data: {}", ft.data());
1221                     }
1222                 }
1223             }
1224         }
1225     }
1226
1227 }