diff options
Diffstat (limited to 'dcaedt_catalog/api')
5 files changed, 1961 insertions, 0 deletions
diff --git a/dcaedt_catalog/api/pom.xml b/dcaedt_catalog/api/pom.xml new file mode 100644 index 0000000..234f12f --- /dev/null +++ b/dcaedt_catalog/api/pom.xml @@ -0,0 +1,198 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onap.sdc.dcae</groupId> + <artifactId>DCAE-DT-Catalog</artifactId> + <version>1806.0.1-SNAPSHOT</version> + </parent> + <artifactId>DCAE-DT-Catalog-API</artifactId> + <packaging>jar</packaging> + <name>DCAE DT Catalog API</name> + + <build> + <sourceDirectory>src/main/java</sourceDirectory> + <testSourceDirectory>src/test/java</testSourceDirectory> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.1</version> + <configuration> + <source>1.8</source> + <target>1.8</target> + <encoding>${project.build.sourceEncoding}</encoding> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <version>2.10</version> + <executions> + <execution> + <id>copy-dependencies</id> + <phase>package</phase> + <goals> + <goal>copy-dependencies</goal> + </goals> + <configuration> + <outputDirectory>${project.build.directory}/deps</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>false</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>buildnumber-maven-plugin</artifactId> + <version>1.4</version> + <executions> + <execution> + <phase>validate</phase> + <goals> + <goal>create</goal> + </goals> + </execution> + </executions> + <configuration> + <doCheck>false</doCheck> + <doUpdate>false</doUpdate> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>2.1</version> + <configuration> + <archive> + <manifest> + <addDefaultImplementationEntries>true</addDefaultImplementationEntries> + </manifest> + <manifestEntries> + <Implementation-Build>${buildNumber}</Implementation-Build> + </manifestEntries> + </archive> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>3.0.2</version> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.6</version> + <configuration> + <descriptorRefs> + <descriptorRef>jar-with-dependencies</descriptorRef> + </descriptorRefs> + <archive> + <manifest> + <mainClass>org.onap.sdc.dcae.catalog.test.Cataloged</mainClass> + </manifest> + <manifestEntries> + <Implementation-Build>${buildNumber}</Implementation-Build> + </manifestEntries> + </archive> + </configuration> + <!-- <executions> <execution> <id>make-assembly</id> this is used for + inheritance merges <phase>package</phase> bind to the packaging phase <goals> + <goal>single</goal> </goals> </execution> </executions> --> + </plugin> + </plugins> + </build> + <repositories> + <repository> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>jcenter</id> + <name>Bintray JCenter</name> + <url>http://repo1.maven.org/maven2/</url> + </repository> + </repositories> + <dependencies> + <dependency> + <groupId>commons-jxpath</groupId> + <artifactId>commons-jxpath</artifactId> + <version>1.3</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.5</version> + </dependency> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpasyncclient</artifactId> + <version>4.1</version> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.4</version> + </dependency> + <dependency> + <groupId>commons-cli</groupId> + <artifactId>commons-cli</artifactId> + <version>1.3</version> + </dependency> + + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>20160810</version> + </dependency> + + <dependency> + <groupId>org.onap.sdc.dcae</groupId> + <artifactId>DCAE-DT-Catalog-Commons</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.sdc.dcae</groupId> + <artifactId>DCAE-DT-Catalog-ASDC</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.sdc.dcae</groupId> + <artifactId>DCAE-DT-Validator-Checker</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>1.10.19</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <!-- use 2.8.0 for Java 7 projects --> + <version>3.8.0</version> + <scope>test</scope> + </dependency> + + + </dependencies> +</project> diff --git a/dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/Catalog.java b/dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/Catalog.java new file mode 100644 index 0000000..b73bb09 --- /dev/null +++ b/dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/Catalog.java @@ -0,0 +1,440 @@ +package org.onap.sdc.dcae.catalog; + +import java.net.URI; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.LinkedList; +import java.util.HashMap; +import java.util.EnumSet; + +import org.json.JSONObject; +import org.onap.sdc.dcae.catalog.commons.Action; +import org.onap.sdc.dcae.catalog.commons.Future; +import org.onap.sdc.dcae.catalog.commons.Futures; +import org.onap.sdc.dcae.catalog.commons.Proxies; + + +import org.json.JSONArray; + +/* + * + */ +public interface Catalog { + + + public abstract URI getUri(); + + public abstract String namespace(); + + public abstract boolean same(Catalog theCatalog); + + public abstract <T> T proxy(JSONObject theData, Class<T> theType); + + + /* Base class for all Catalog objects. + */ + public static interface Element<T extends Element<T>> { + + /** + * provide a typed 'self' reference + */ + public default T self() { return (T)this; } + + /** + */ + public default Class<T> selfClass() { + return (Class<T>)getClass().getInterfaces()[0]; + } + + /* */ + public Catalog catalog(); + + /** + */ + public String id(); + + /** + * Direct access to the underlying JSON object. + * Warning: Modifications to the JSON object are reflected in the Element. + */ + public JSONObject data(); + + /** + * Provides the labels of the artifacts (we use labels to type/classify the + * neo4j artifacts, nodes and edges. + * Currently not all queries retrieve the labels. + */ + public String[] labels(); + + /* Allows for typed deep exploration of the backing JSON data structure + * <pre> + * {@code + * element("type", Type.class); + * } + * </pre> + * + * @arg theName name of a JSON entry ; It must map another JSONObject. + * @arg theType the expected wrapping catalog artifact type + * @return the JSON entry wrapped in the specified type + */ + public default <E extends Element<E>> E element(String theName, Class<E> theType) { + JSONObject elemData = data().optJSONObject(theName); + if (elemData == null) + return null; + else + return catalog().proxy(elemData, theType); + } + + /* Similar to {@link #element(String,Class)} but for collection wrapping. + * Example: + * <pre> + * {@code + * element("nodes", Nodes.class); + * } + * </pre> + */ + public default <E extends Elements> E elements(String theName, Class<E> theType) { + //throws ReflectiveOperationException { + JSONArray elemsData = data().optJSONArray(theName); + if (elemsData == null) { + return null; + } + else { + Class etype = Proxies.typeArgument(theType); + Elements elems = null; + try { + elems = theType.newInstance(); + } + catch (ReflectiveOperationException rox) { + throw new RuntimeException("Failed to instantiate " + theType, rox); + } + + try{ + for (Iterator i = elemsData.iterator(); i.hasNext();) { + JSONObject elemData = (JSONObject)i.next(); + elems.add(catalog().proxy(elemData, etype)); + } + } + catch(Exception e){ + throw new RuntimeException("Failed to fetch json data ", e); + } + return (E)elems; + } + } + + /* + */ + public default boolean same(Element theElem) { + return this.catalog().same(theElem.catalog()) && + this.id().equals(theElem.id()); + } + } + + /* + * Base class for all collections of elements. + */ + public static class Elements<T extends Element> + extends LinkedList<T> { + + public String toString() { + StringBuilder sb = new StringBuilder("["); + for (Element el: this) { + sb.append(el.selfClass().getSimpleName()) + .append("(") + .append(el.data()) + .append("),"); + } + sb.append("]"); + return sb.toString(); + } + } + + /* + * We need this contraption in order to store a mix of Folders and CatalogItem + * instances (Elements in self is not good because it is defined around a + * type variable so we cannot use reflection to determine the type at runtime + * - generics are resolved compile time) + */ + public static class Mixels extends Elements<Element> { + } + + /* + */ + public static interface Item<T extends Item<T>> extends Element<T> { + + public String name(); + + public String description(); + + /* catalog item native identifier */ + public String itemId(); + + /* similar to @ItemAction#withModels + */ + default public Future<Templates> models() { + Templates t = elements("models", Templates.class); + if (t != null) + return Futures.succeededFuture(t); + else + return Futures.advance(catalog().item(itemId()) + .withModels() + .execute(), + item -> (Templates)item.elements("models", Templates.class)); + } + + /* similar to @ItemAction#withAnnotations + */ + default public Future<Annotations> annotations() { + Annotations a = elements("annotations", Annotations.class); + if (a != null) + return Futures.succeededFuture(a); + else + return Futures.advance(catalog().item(itemId()) + .withAnnotations() + .execute(), + item -> (Annotations)item.elements("annotations", Annotations.class)); + } + } + + /* + * Collection of catalog items. + */ + public static class Items extends Elements<Item> { + } + + /* + */ + public static interface Folder extends Element<Folder> { + + public String name(); + + public String description(); + + public String itemId(); + + /* the namespace is immutable */ + public default String namespace() { + return catalog().namespace(); + } + + /* + */ + default public Future<Items> items() { + Items i = elements("items", Items.class); + if (i != null) + return Futures.succeededFuture(i); + else + return Futures.advance(catalog().folder(itemId()) + .withItems() + .execute(), + folder -> (Items)folder.elements("items", Items.class)); + } + + /* + */ + default public Future<Folders> parts() { + Folders f = elements("parts", Folders.class); + if (f != null) + return Futures.succeededFuture(f); + else + return Futures.advance(catalog().folder(itemId()) + .withParts() + .execute(), + folder -> (Folders)folder.elements("parts", Folders.class)); + } + + /* + */ + public Future<Folders> partof(); + + } + + + public static class Folders extends Elements<Folder> { + } + + //no predefined properties here + public static interface Annotation extends Element<Annotation> { + + public default String namespace() { + return catalog().namespace(); + } + } + + public static class Annotations extends Elements<Annotation> { + } + + /** + * A TOSCA teamplate. + * When a deep loading method is used to obtain a Template its collection + * of inputs and nodes will be immediately available (and 'cached' within + * the backing JSON object). It can be retrieved through a call to + * {@link Element#elements(String,Class)} as in: + * elements("inputs", Inputs.class) + * or + * elements("nodes", Nodes.class) + * + * The same result will be obtained through one of the methods of the + * navigation interface, {@link #inputs()} or {@link #nodes()}; in this case + * the result does not become part of the backing JSONObject. + */ + public static interface Template extends Element<Template> { + + public String name(); + + public String version(); + + public String description(); + + } + + /** + * Collection of {@link Catalog.Template template} instances. + */ + public static class Templates extends Elements<Template> { + } + + + /** + * A TOSCA type declaration. + */ + public interface Type extends Element<Type> { + + public String name(); + + /** + * Allows navigation to the parent {@link Catalog.Type type}, if any. + */ + public Future<Type> derivedfrom(); + + } + + /** + * Collection of {@link Catalog.Type type} instances. + */ + public static class Types extends Elements<Type> { + } + + + public static interface TemplateAction extends Action<Template> { + + public TemplateAction withInputs(); + + public TemplateAction withOutputs(); + + public TemplateAction withNodes(); + + public TemplateAction withNodeProperties(); + + public TemplateAction withNodeRequirements(); + + public TemplateAction withNodePropertiesAssignments(); + + public TemplateAction withNodeCapabilities(); + + public TemplateAction withNodeCapabilityProperties(); + + public TemplateAction withNodeCapabilityPropertyAssignments(); + + public TemplateAction withPolicies(); + + public TemplateAction withPolicyProperties(); + + public TemplateAction withPolicyPropertiesAssignments(); + + @Override + public Future<Template> execute(); + + } + + /* + */ + public static interface TypeAction extends Action<Type> { + + public TypeAction withHierarchy(); + + public TypeAction withRequirements(); + + public TypeAction withCapabilities(); + + @Override + public Future<Type> execute(); + + } + + /* + */ + public static interface FolderAction extends Action<Folder> { + + public FolderAction withAnnotations(); + + public FolderAction withAnnotations(String theSelector); + + public FolderAction withItems(); + + public FolderAction withItemAnnotations(); + + public FolderAction withItemAnnotations(String theSelector); + + public FolderAction withItemModels(); + + public FolderAction withParts(); + + public FolderAction withPartAnnotations(); + + public FolderAction withPartAnnotations(String theSelector); + + @Override + public Future<Folder> execute(); + } + + /* + */ + public static interface ItemAction<T extends Item> extends Action<T> { + + public ItemAction<T> withModels(); + + public ItemAction<T> withAnnotations(); + + @Override + public Future<T> execute(); + + } + + /** + */ + public abstract Future<Folders> roots(); + + /** + */ + public abstract Future<Folders> rootsByLabel(String theLabel); + + /** + */ + public abstract Future<Mixels> lookup(JSONObject theSelector); + + public abstract Future<Mixels> lookup(String theAnnotation, JSONObject theSelector); + + /** + */ + public abstract FolderAction folder(String theFolderId); + + /** + */ + public abstract <T extends Item> ItemAction<T> item(String theItemId); + + /** + */ + public abstract TemplateAction template(String theTemplateId); + + /** + */ + public abstract TypeAction type(String theNamespace, String theTypeName); + + + +} diff --git a/dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/asdc/ASDCCatalog.java b/dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/asdc/ASDCCatalog.java new file mode 100644 index 0000000..e08f3a6 --- /dev/null +++ b/dcaedt_catalog/api/src/main/java/org/onap/sdc/dcae/catalog/asdc/ASDCCatalog.java @@ -0,0 +1,1227 @@ +package org.onap.sdc.dcae.catalog.asdc; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.io.IOUtils; +import org.apache.commons.jxpath.JXPathContext; +import org.apache.commons.jxpath.JXPathNotFoundException; +import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONObject; +import org.onap.sdc.common.onaplog.Enums.LogLevel; +import org.onap.sdc.common.onaplog.OnapLoggerDebug; +import org.onap.sdc.dcae.catalog.Catalog; +import org.onap.sdc.dcae.catalog.commons.*; +import org.onap.sdc.dcae.checker.*; + +import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; +import java.util.function.BiFunction; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class ASDCCatalog implements Catalog { + + private + static final String JXPATH_NOT_FOUND_EXCEPTION = "JXPathNotFoundException {}"; + private + static final String OCCURRENCES = "occurrences"; + private + static final String TOPOLOGY_TEMPLATE_NODE_TEMPLATES = "/topology_template/node_templates"; + private + static final String NODES_NAME = "/nodes[name='"; + private + static final String ITEM_ID = "itemId"; + private + static final String LABELS = "labels"; + private + static final String ARTIFACT_URL = "artifactURL"; + private + static final String CAPABILITY = "capability"; + private + static final String DATABASE = "Database"; + private + static final String COLLECTOR = "Collector"; + private + static final String MICROSERVICE = "Microservice"; + private + static final String ANALYTICS = "Analytics"; + private + static final String POLICY = "Policy"; + private + static final String SOURCE = "Source"; + private + static final String UTILITY = "Utility"; + private + static final String NAME = "name"; + private + static final String ID = "id"; + private + static final String ARTIFACT_NAME = "artifactName"; + private + static final String DESCRIPTION = "description"; + private + static final String MODELS = "models"; + private + static final String ARTIFACTS = "artifacts"; + private + static final String ITEMS = "items"; + private + static final String PROPERTIES = "']/properties"; + private + static final String TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 = "/topology_template/node_templates/"; + private + static final String PROPERTIES_NAME = "']/properties[name='"; + private + static final String CAPABILITIES = "']/capabilities"; + private + static final String CAPABILITIES_NAME = "']/capabilities[name='"; + + private static OnapLoggerDebug debugLogger = OnapLoggerDebug.getInstance(); + + private ASDC asdc; + + private JSONObject folders = new JSONObject(); + private String[] folderFields = new String[] {ID, ITEM_ID, NAME}; + + private ProxyBuilder proxies; + private Map<Target, JXPathContext> contexts = new HashMap<Target, JXPathContext>(); + + // resource and its catalog + private Map<UUID, org.onap.sdc.dcae.checker.Catalog> catalogs = new HashMap<UUID, org.onap.sdc.dcae.checker.Catalog>(); + + public ASDCCatalog(URI theURI) { + + this.asdc = new ASDC(); + this.asdc.setUri(theURI); + + initFolders(); + + this.proxies = new ProxyBuilder().withConverter(v -> v == null ? null : UUID.fromString(v.toString()), UUID.class) + .withExtensions( + new ImmutableMap.Builder<String, BiFunction<Proxy, Object[], Object>>().put("data", (proxy, args) -> proxy.data()) + .build()).withContext(new ImmutableMap.Builder<String, Object>().put("catalog", this).build()); + } + + private void initFolders() { + + JSONArray labels = new JSONArray(); + labels.put("Folder"); + labels.put("DCAE"); + labels.put("Superportfolio"); // for CCD compatibility + + folders.put(DATABASE, new JSONObject().put(NAME, DATABASE).put(ID, "dcae_database") + .put(ITEM_ID, DATABASE).put(LABELS, labels)); + folders.put(COLLECTOR, new JSONObject().put(NAME, COLLECTOR).put(ID, "dcae_collector") + .put(ITEM_ID, COLLECTOR).put(LABELS, labels)); + folders.put(MICROSERVICE, new JSONObject().put(NAME, MICROSERVICE).put(ID, "dcae_microservice") + .put(ITEM_ID, MICROSERVICE).put(LABELS, labels)); + folders.put(ANALYTICS, new JSONObject().put(NAME, ANALYTICS).put(ID, "dcae_analytics") + .put(ITEM_ID, ANALYTICS).put(LABELS, labels)); + folders.put(POLICY, new JSONObject().put(NAME, POLICY).put(ID, "dcae_policy").put(ITEM_ID, POLICY) + .put(LABELS, labels)); + folders.put(SOURCE, new JSONObject().put(NAME, SOURCE).put(ID, "dcae_source").put(ITEM_ID, SOURCE) + .put(LABELS, labels)); + folders.put(UTILITY, new JSONObject().put(NAME, UTILITY).put(ID, "dcae_utility") + .put(ITEM_ID, UTILITY).put(LABELS, labels)); + } + + public URI getUri() { + return this.asdc.getUri(); + } + + public String namespace() { + return "asdc"; + } + + public boolean same(Catalog theCatalog) { + return true; + } + + public <T> T proxy(JSONObject theData, Class<T> theType) { + return proxies.build(theData, theType); + } + + /** */ + public Future<Folders> roots() { + + Folders roots = new Folders(); + for (Iterator fi = folders.keys(); fi.hasNext();) { + roots.add(proxies.build(folders.getJSONObject((String) fi.next()), Folder.class)); + } + return Futures.succeededFuture(roots); + } + + /** */ + public Future<Folders> rootsByLabel(String theLabel) { + + Folders roots = new Folders(); + for (Iterator fi = folders.keys(); fi.hasNext();) { + JSONObject folder = folders.getJSONObject((String) fi.next()); + JSONArray labels = folder.getJSONArray(LABELS); + + for (int i = 0; i < labels.length(); i++) { + if (labels.get(i).equals(theLabel)) { + roots.add(proxies.build(folder, Folder.class)); + } + } + } + return Futures.succeededFuture(roots); + } + + /** */ + public Future<Mixels> lookup(JSONObject theSelector) { + return Futures.succeededFuture(new Mixels()); + } + + public Future<Mixels> lookup(String theAnnotation, JSONObject theSelector) { + return Futures.succeededFuture(new Mixels()); + } + + /** */ + public ItemAction item(String theItemId) { + return new ResourceAction(UUID.fromString(theItemId)); + } + + /** */ + public FolderAction folder(String theFolderId) { + return new FolderAction(theFolderId); + } + + public TemplateAction template(String theId) { + return new TemplateAction(theId); + } + + public TypeAction type(String theItemId, String theName) { + return new TypeAction(UUID.fromString(theItemId), theName); + } + + protected static String resolveTargetName(Target theTarget) { + return (String) ((Map) ((Map) theTarget.getTarget()).get("metadata")).get("template_name"); + } + + protected Object resolve(Target theTarget, String thePath) { + try { + return contexts.get(theTarget).getValue(thePath); + } catch (JXPathNotFoundException pnfx) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "JXPathNotFoundException {}", pnfx); + return null; + } + } + + // covers common TOSCA pattern of single entry maps + public Map.Entry<String, Map> toEntry(Object theValue) { + return (Map.Entry<String, Map>) ((Map) theValue).entrySet().iterator().next(); + } + + protected Map selectEntries(Map theOriginal, String... theKeys) { + Arrays.sort(theKeys); + Map selection = ((Set<Map.Entry>) theOriginal.entrySet()).stream() + .filter(e -> Arrays.binarySearch(theKeys, e.getKey().toString()) >= 0) + .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); + return selection; + } + + protected Map evictEntries(Map theOriginal, String... theKeys) { + Arrays.sort(theKeys); + Map selection = ((Set<Map.Entry>) theOriginal.entrySet()).stream() + .filter(e -> Arrays.binarySearch(theKeys, e.getKey().toString()) < 0) + .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); + return selection; + } + + protected MapBuilder renderEntry(Map.Entry theEntry, String... theKeys) { + MapBuilder out = new MapBuilder(); + out.put(NAME, theEntry.getKey()); + + for (String key : theKeys) { + out.put(key, ((Map) theEntry.getValue()).get(key)); + } + return out; + } + + protected <T> Stream<T> stream(Iterator<T> theSource) { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(theSource, + Spliterator.NONNULL | Spliterator.DISTINCT | Spliterator.IMMUTABLE), false); + } + + private JSONArray selectModels(JSONArray theArtifacts) { + JSONArray models = new JSONArray(); + if (theArtifacts == null) { + return models; + } + + for (int i = 0; i < theArtifacts.length(); i++) { + JSONObject artifact = theArtifacts.getJSONObject(i); + String name = artifact.optString(ARTIFACT_NAME); + if (name != null && StringUtils.containsIgnoreCase(name, "template")) { + models.put(new JSONObject().putOpt(NAME, artifact.optString(ARTIFACT_NAME)) + .putOpt("version", artifact.optString("artifactVersion")) + .putOpt(DESCRIPTION, artifact.optString("artifactType")) + .putOpt(ID, artifact.optString(ARTIFACT_URL)) + .putOpt(ITEM_ID, artifact.optString(ARTIFACT_URL))); + } + } + return models; + } + + private JSONObject patchResource(JSONObject theResource) { + + theResource.remove("resources"); + theResource.putOpt(ID, theResource.opt("uuid")); + theResource.putOpt(ITEM_ID, theResource.opt("uuid")); + + return theResource; + } + + private static void dumpTargets(String theDirName, Collection<Target> theTargets) { + try { + File targetDir = new File(theDirName); + if (!targetDir.exists() && !targetDir.mkdirs()) { + throw new IllegalStateException("Couldn't create dir: " + theDirName); + } + for (Target t : theTargets) { + FileWriter dump = new FileWriter(new File(theDirName, t.getName())); + IOUtils.copy(t.open(), dump); + dump.close(); + } + } catch (IOException iox) { + debugLogger.log(LogLevel.DEBUG,"ASDCCatalog", "IOException {}", iox); + } + } + + private static URI asURI(String theValue) { + try { + return new URI(theValue); + } catch (URISyntaxException urisx) { + throw new IllegalArgumentException("Invalid URI", urisx); + } + } + + private static UUID asUUID(String theValue) { + return UUID.fromString(theValue); + } + + private org.onap.sdc.dcae.checker.Catalog getCatalog(UUID theResourceId) { + return this.catalogs.get(theResourceId); + } + + private String getArtifactVersion(JSONObject theData) { + return theData.getString("artifactVersion"); + } + + private String getArtifactName(JSONObject theData) { + return theData.getString(ARTIFACT_NAME); + } + + private String getArtifactURL(JSONObject theData) { + return theData.getString(ARTIFACT_URL); + } + + private URI getArtifactURI(JSONObject theData) { + return asURI(theData.getString(ARTIFACT_URL)); + } + + /** */ + public class ResourceAction implements Catalog.ItemAction<Resource> { + + private UUID iid; + private boolean doModels; + + ResourceAction(UUID theItemId) { + this.iid = theItemId; + } + + public ResourceAction withModels() { + this.doModels = true; + return this; + } + + public ResourceAction withAnnotations() { + return this; + } + + @Override + public Future<Resource> execute() { + + return Futures.advance(asdc.getResource(this.iid, JSONObject.class), resourceData -> { + if (doModels) { + resourceData.put(MODELS, selectModels(resourceData.optJSONArray(ARTIFACTS))); + } + return proxies.build(patchResource(resourceData), Resource.class); + }); + } + + protected Future<JSONObject> executeRaw() { + + return Futures.advance(asdc.getResource(this.iid, JSONObject.class), resourceData -> { + if (doModels) { + resourceData.put(MODELS, selectModels(resourceData.optJSONArray(ARTIFACTS))); + } + return resourceData; + }, resourceError -> new RuntimeException("Failed to retrieve item " + this.iid, resourceError)); + } + } + + public class FolderAction implements Catalog.FolderAction { + + private boolean doItemModels; + private String folderName; + + // use the id/UUID of the folder ?? + private FolderAction(String theFolderName) { + this.folderName = theFolderName; + } + + public FolderAction withAnnotations() { + return this; + } + + public FolderAction withAnnotations(String theSelector) { + return this; + } + + public FolderAction withItems() { + return this; + } + + public FolderAction withItemAnnotations() { + return this; + } + + public FolderAction withItemAnnotations(String theSelector) { + return this; + } + + public FolderAction withItemModels() { + doItemModels = true; + return this; + } + + public FolderAction withParts() { + return this; + } + + public FolderAction withPartAnnotations() { + return this; + } + + public FolderAction withPartAnnotations(String theSelector) { + return this; + } + + @Override + public Future<Folder> execute() { + + JSONObject folder = folders.optJSONObject(this.folderName); + if (folder == null) { + return Futures.failedFuture(new RuntimeException("No such folder " + this.folderName)); + } + + final JSONObject folderView = new JSONObject(folder, folderFields); + + return Futures.advance(asdc.getResources(JSONArray.class, "DCAE Component", this.folderName), + resourcesData -> { + + Actions.CompoundAction<Resource> itemsAction = new Actions.BasicCompoundAction<Resource>(); + for (int i = 0; i < resourcesData.length(); i++) { + JSONObject resource = resourcesData.getJSONObject(i); + + if (doItemModels) { + itemsAction + .addAction(new ResourceAction(asUUID(resource.getString("uuid"))).withModels()); + } else { + folderView.append(ITEMS, patchResource(resource)); + } + } + + try { + List<Resource> items = itemsAction.execute().waitForResult(); + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Number of DCAE item for : {} is {}", this.folderName, items.size()); + + for (Resource res : filterLatestVersion(items)) { + folderView.append(ITEMS, patchResource(res.data())); + } + } catch (Exception x) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Exception {}", x); + throw new RuntimeException("Failed to retrieve folder items", x); + } + + return proxies.build(folderView, Folder.class); + }, resourcesError -> new RuntimeException("Failed to retrieve resources", resourcesError)); + } + + public Collection<Resource> filterLatestVersion(Collection<Resource> items) throws IllegalArgumentException { + if (items == null) { + throw new IllegalArgumentException("null is not acceptable as a list of items"); + } + Map<UUID, Resource> itemsMap = new HashMap<UUID, Resource>(items.size()); + for (Resource r : items) { + if (itemsMap.containsKey(r.invariantUUID()) && isNewerVersion(itemsMap, r)) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Avoiding adding item {} since it has a advanced version already", r.toString()); + continue; + } + itemsMap.put(r.invariantUUID(), r); + } + return itemsMap.values(); + } + + private boolean isNewerVersion(Map<UUID, Resource> itemsMap, Resource r) { + return Float.valueOf(itemsMap.get(r.invariantUUID()).version()) > Float.valueOf(r.version()); + } + + } + + /** */ + public class TemplateAction implements Catalog.TemplateAction { + + private String artifactId; + private Target target; + private org.onap.sdc.dcae.checker.Catalog catalog; + private JXPathContext ctx = JXPathContext.newContext(new HashMap()); + + private boolean doNodes, doNodeProperties, doNodePropertiesAssignments, doNodeRequirements, doNodeCapabilities, + doNodeCapabilityProperties, doNodeCapabilityPropertyAssignments; + + protected TemplateAction(Target theTarget) { + this.target = theTarget; + } + + /* + * expected to be the relative url provided by asdc for the template + * artifact + */ + protected TemplateAction(String theArtifactId) { + this.artifactId = theArtifactId; + } + + public TemplateAction withInputs() { + return this; + } + + public TemplateAction withOutputs() { + return this; + } + + public TemplateAction withNodes() { + this.doNodes = true; + return this; + } + + protected TemplateAction doNodes() { + if (!this.doNodes) { + return this; + } + + Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES); + if (nodes == null) { + return this; + } + + ctx.setValue("/nodes", + nodes.entrySet().stream() + .map(nodeEntry -> new MapBuilder().put(NAME, ((Map.Entry) nodeEntry).getKey()) + .put(DESCRIPTION, this.artifactId) + .putAll(selectEntries((Map) ((Map.Entry) nodeEntry).getValue(), "type")).build()) + .collect(Collectors.toList())); + + return this; + } + + // pre-requisite: a call to 'withNodes' + public TemplateAction withNodeProperties() { + this.doNodeProperties = true; + return this; + } + + protected TemplateAction doNodeProperties() { + if (!this.doNodeProperties) { + return this; + } + + Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES); + if (nodes == null) { + return this; + } + + nodes.entrySet().stream().forEach(node -> ctx.setValue( + NODES_NAME + ((Map.Entry) node).getKey() + PROPERTIES, + stream(catalog.facets(Construct.Node, Facet.properties, + ((Map) ((Map.Entry) node).getValue()).get("type").toString())) + .map(propEntry -> new MapBuilder().put(NAME, propEntry.getKey()) + .putAll((Map) propEntry.getValue()).build()) + .collect(Collectors.toList()))); + + return this; + } + + // pre-requisite: a call to 'withNodesProperties' + public TemplateAction withNodePropertiesAssignments() { + this.doNodePropertiesAssignments = true; + return this; + } + + protected TemplateAction doNodePropertiesAssignments() { + if (!this.doNodePropertiesAssignments) { + return this; + } + + Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES); + if (nodes == null) { + return this; + } + + nodes.entrySet().stream().forEach(node -> { + List nodeProps = null; + try { + nodeProps = (List) ctx.getValue(NODES_NAME + ((Map.Entry) node).getKey() + PROPERTIES); + } catch (JXPathNotFoundException pnfx) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx); + return; + } + + nodeProps.stream().forEach(prop -> { + // pick from + String propPath = TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 + ((Map.Entry) node).getKey() + + "/properties/" + ((Map) prop).get(NAME); + Object propValue = resolve(this.target, propPath); + // to conform with the db based api we should analyze the + // value for function calls + // dump at .. + propPath = NODES_NAME + ((Map.Entry) node).getKey() + PROPERTIES_NAME + + ((Map) prop).get(NAME) + "']"; + if (propValue != null) { + ctx.setValue(propPath + "/assignment", + new ImmutableMap.Builder().put("value", propValue).build()); + } + }); + }); + + return this; + } + + protected Map renderRequirementDefinition(Map.Entry theReq) { + Map def = (Map) theReq.getValue(); + return new MapBuilder().put(NAME, theReq.getKey()) + // capability must be present + .put(CAPABILITY, + new MapBuilder().put(NAME, def.get(CAPABILITY)) + .put(ID, this.target.getName() + "/" + def.get(CAPABILITY)).build()) + .putAll(evictEntries(def, CAPABILITY)).build(); + } + + // TODO: see how this comes out of neo and match it + protected Map renderRequirementAssignment(Map.Entry theReq) { + Map def = (Map) theReq.getValue(); + return new MapBuilder().put(NAME, theReq.getKey()) + // capability must be present + .put(CAPABILITY, + new MapBuilder().put(NAME, def.get(CAPABILITY)) + // we provide an id only if the capability + // points to a type + .putOpt(ID, + catalog.hasType(Construct.Capability, (String) def.get(CAPABILITY)) + ? (this.target.getName() + "/" + def.get(CAPABILITY)) : null) + .build()) + .putAll(evictEntries(def, CAPABILITY)).build(); + } + + public TemplateAction withNodeRequirements() { + this.doNodeRequirements = true; + return this; + } + + TemplateAction doNodeRequirements() { + if (!this.doNodeRequirements) { + return this; + } + + // requirements come first from the type and then can be further + // refined by their assignment within the + // node template + Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES); + if (nodes == null) { + return this; + } + + // type + nodes.entrySet().stream() + .forEach( + node -> ctx + .setValue( + NODES_NAME + + ((Map.Entry) node) + .getKey() + + "']/requirements", + StreamSupport + .stream(Spliterators.spliteratorUnknownSize( + catalog.requirements(((Map) ((Map.Entry) node).getValue()) + .get("type").toString()), + Spliterator.NONNULL | Spliterator.DISTINCT + | Spliterator.IMMUTABLE), + false) + .map((Map.Entry reqEntry) -> renderRequirementDefinition(reqEntry)) + .collect(Collectors.toList()))); + + // merge assignments on top of definitions + nodes.entrySet().stream().forEach(node -> { + List nodeReqsAssigns = (List) resolve(this.target, + TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 + ((Map.Entry) node).getKey() + "/requirements"); + if (nodeReqsAssigns == null) { + return; + } + nodeReqsAssigns.stream().forEach(req -> { + Map.Entry reqAssign = toEntry(req); + catalog.mergeDefinitions((Map) ctx.getValue(NODES_NAME + ((Map.Entry) node).getKey() + + "']/requirements[name='" + reqAssign.getKey() + "']"), + renderRequirementAssignment(reqAssign)); + }); + }); + + return this; + } + + public TemplateAction withNodeCapabilities() { + this.doNodeCapabilities = true; + return this; + } + + protected Map renderCapabilityDefinition(Map.Entry theCap) { + Map def = (Map) theCap.getValue(); + return new MapBuilder().put(NAME, theCap.getKey()) + .put("type", + new MapBuilder().put(NAME, def.get("type")) + .put(ID, this.target.getName() + "/" + def.get("type")).build()) + .putAll(evictEntries(def, "properties", "type")).build(); + } + + TemplateAction doNodeCapabilities() { + if (!this.doNodeCapabilities) { + return this; + } + + Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES); + if (nodes == null) { + return this; + } + + // collect capabilities through the node type hierarchy + + // we evict the properties from the node type capability declaration + // (when declaring a capability with the + // node type some re-definition of capability properties can take + // place). + nodes.entrySet().stream() + .forEach(node -> ctx.setValue(NODES_NAME + ((Map.Entry) node).getKey() + CAPABILITIES, + + stream(catalog.facets(Construct.Node, Facet.capabilities, + ((Map) ((Map.Entry) node).getValue()).get("type").toString())) + .map((Map.Entry capEntry) -> renderCapabilityDefinition(capEntry)) + .collect(Collectors.toList()))); + + return this; + } + + public TemplateAction withNodeCapabilityProperties() { + this.doNodeCapabilityProperties = true; + return this; + } + + TemplateAction doNodeCapabilityProperties() { + + if (!this.doNodeCapabilityProperties) { + return this; + } + + Map nodes = (Map) resolve(this.target, TOPOLOGY_TEMPLATE_NODE_TEMPLATES); + if (nodes == null) { + return this; + } + + // pick up all the properties from the capability type hierarchy + // definition + nodes.entrySet().stream().forEach(node -> { + List nodeCapabilities = (List) ctx + .getValue(NODES_NAME + ((Map.Entry) node).getKey() + CAPABILITIES); + if (nodeCapabilities == null) { + return; + } + + // collect properties from the capability type hierarchy + nodeCapabilities.stream().forEach(capability -> { + List capabilityProperties = StreamSupport + .stream(Spliterators.spliteratorUnknownSize( + catalog.facets(Construct.Capability, Facet.properties, + ((Map)((Map)capability).get("type")).get(NAME).toString()), + Spliterator.NONNULL | Spliterator.DISTINCT | Spliterator.IMMUTABLE), false) + .map((Map.Entry capEntry) -> new MapBuilder().put(NAME, capEntry.getKey()) + .putAll((Map) capEntry.getValue()).build()) + .collect(Collectors.toList()); + + if (!capabilityProperties.isEmpty()) { + ctx.setValue(NODES_NAME + ((Map.Entry) node).getKey() + CAPABILITIES_NAME + + ((Map) capability).get(NAME) + PROPERTIES, capabilityProperties); + } + }); + + // and go over the node type (hierarchy) and pick up any + // re-definitions from there. + StreamSupport + .stream(Spliterators.spliteratorUnknownSize( + catalog.facets(Construct.Node, Facet.capabilities, + ((Map) ((Map.Entry) node).getValue()).get("type").toString()), + Spliterator.NONNULL | Spliterator.DISTINCT | Spliterator.IMMUTABLE), false) + .forEach((Map.Entry capability) -> { + // for each capability property that has some node + // type level re-definition + Map properties = (Map) ((Map) capability.getValue()).get("properties"); + if (properties == null) { + return; + } + + properties.entrySet().stream().forEach(property -> { + String propertyLoc = NODES_NAME + ((Map.Entry) node).getKey() + + CAPABILITIES_NAME + ((Map) capability).get(NAME) + + PROPERTIES_NAME + ((Map.Entry) property).getKey() + "']"; + ctx.setValue(propertyLoc, catalog.mergeDefinitions((Map) ctx.getValue(propertyLoc), + (Map) ((Map.Entry) property).getValue())); + }); + }); + }); + + return this; + } + + public TemplateAction withNodeCapabilityPropertyAssignments() { + this.doNodeCapabilityPropertyAssignments = true; + return this; + } + + TemplateAction doNodeCapabilityPropertyAssignments() { + if (!this.doNodeCapabilityPropertyAssignments) { + return this; + } + + // this is a wasteful: we go over all declared + // nodes/capabilities/properties and check if there is an assigned + // value in the actual template. It is optimal to approach the + // problem from the other direction: go over delared + // assignments and set them in the output structure .. + + List nodes = null; + try { + nodes = (List) ctx.getValue("/nodes"); + } catch (JXPathNotFoundException pnfx) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx); + return this; + } + + nodes.stream().forEach(node -> { + List capabilities = (List) ctx.getValue(NODES_NAME + ((Map) node).get(NAME) + CAPABILITIES); + if (capabilities == null) { + return; + } + + capabilities.stream().forEach(capability -> { + List properties = null; + try { + properties = (List) ctx.getValue(NODES_NAME + ((Map) node).get(NAME) + + CAPABILITIES_NAME + ((Map) capability).get(NAME) + PROPERTIES); + } catch (JXPathNotFoundException pnfx) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx); + return; + } + + properties.stream().forEach(property -> { + String location = NODES_NAME + ((Map) node).get(NAME) + CAPABILITIES_NAME + + ((Map) capability).get(NAME) + PROPERTIES_NAME + ((Map) property).get(NAME) + + "']/assignment"; + + // pick the value from the original + try { + Object assignment = resolve(this.target, + TOPOLOGY_TEMPLATE_NODE_TEMPLATES1 + ((Map) node).get(NAME) + "/capabilities/" + + ((Map) capability).get(NAME) + "/properties/" + + ((Map) property).get(NAME)); + if (assignment != null) { + ctx.setValue(location, new ImmutableMap.Builder().put("value", assignment).build()); + } + } catch (JXPathNotFoundException pnfx) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), JXPATH_NOT_FOUND_EXCEPTION, pnfx); + // it's ok, no assignment + } + }); + }); + }); + + return this; + } + + public TemplateAction withPolicies() { + return this; + } + + public TemplateAction withPolicyProperties() { + return this; + } + + public TemplateAction withPolicyPropertiesAssignments() { + return this; + } + + public Future<Template> execute() { + + if (this.target == null) { + + String[] parts = this.artifactId.split("/"); + if (parts.length != 8) { + return Futures + .failedFuture(new Exception("Unexpected artifact id for template " + this.artifactId)); + } + + UUID resourceId = asUUID(parts[5]); + this.catalog = ASDCCatalog.this.catalogs.get(resourceId); + + // if we find a catalog for this resource we have to figure out + // if it contains the required target .. + + try { + JSONObject resource = new ResourceAction(resourceId).executeRaw().waitForResult(); + + Checker checker = new Checker(); + TargetLocator locator = new ASDCLocator(resource.getJSONArray(ARTIFACTS), + ASDCCatalog.this.catalogs.get(resourceId)); + checker.setTargetLocator(locator); + + Target template = locator.resolve("template"); + if (template == null) { + return Futures.failedFuture(new Exception("Failed to locate template in " + resource)); + } + + checker.check(template); + + for (Target t : checker.targets()) { + if (t.getReport().hasErrors()) { + dumpTargets(resourceId.toString(), checker.targets()); + return Futures.failedFuture(new Exception("Failed template validation: " + t.getReport())); + } + } + + this.target = template; + this.catalog = checker.catalog(); + ASDCCatalog.this.catalogs.put(resourceId, this.catalog); + // we should only be doing this if we discovered an update + // (by checking timestampts). Actually, we should + // only do the artifact fetching if we detect an update + ASDCCatalog.this.contexts.put(template, JXPathContext.newContext(template.getTarget())); + } catch (Exception x) { + return Futures.failedFuture(x); + } + } + + this.doNodes().doNodeProperties().doNodePropertiesAssignments().doNodeRequirements().doNodeCapabilities() + .doNodeCapabilityProperties().doNodeCapabilityPropertyAssignments(); + + JSONObject pack = new JSONObject((Map) ctx.getContextBean()).put(NAME, this.target.getName().toString()) + .put(ID, this.target.getLocation().toString()) + .put(ITEM_ID, this.target.getLocation().toString()); + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), pack.toString(2)); + + return Futures.succeededFuture(proxies.build(pack, Template.class)); + } + } + + public class TypeAction implements Catalog.TypeAction { + + private String name; + private UUID resourceId; + private JXPathContext ctx; + + private boolean doHierarchy = false, doRequirements = false, doCapabilities = false; + + private TypeAction(UUID theResourceId, /* Construct theConstruct, */ String theName) { + this.resourceId = theResourceId; + this.name = theName; + } + + public TypeAction withHierarchy() { + this.doHierarchy = true; + return this; + } + + TypeAction doHierarchy(org.onap.sdc.dcae.checker.Catalog theCatalog) { + if (!this.doHierarchy) { + return this; + } + + ctx.setValue("/hierarchy", + stream(theCatalog.hierarchy(Construct.Node, this.name)).skip(1) // skip + // self + .map((Map.Entry type) -> new MapBuilder() + .put(NAME, type.getKey()).put(ID, resourceId + "/" + type.getKey()) + .putOpt(DESCRIPTION, ((Map) type.getValue()).get(DESCRIPTION)).build()) + // renderEntry((Map.Entry)type, + // "description").build()) + .collect(Collectors.toList())); + return this; + } + + public TypeAction withRequirements() { + this.doRequirements = true; + return this; + } + + TypeAction doRequirements(org.onap.sdc.dcae.checker.Catalog theCatalog) { + if (!this.doRequirements) { + return this; + } + + ctx.setValue("requirements", stream(theCatalog.requirements(this.name)).map((Map.Entry req) -> { + String capability = (String) ((Map) req.getValue()).get(CAPABILITY), + node = (String) ((Map) req.getValue()).get(CAPABILITY); + return new MapBuilder().put(NAME, req.getKey()).put(ID, resourceId + "/" + req.getKey()) + .put(OCCURRENCES, ((Map) req.getValue()).get(OCCURRENCES)) + .put(CAPABILITY, + new MapBuilder().put(NAME, capability) + // if the capability points to a + // capability type then encode + // the type reference, else it is a name + // (within a node type) + .put(ID, + getCatalog(resourceId).hasType(Construct.Capability, capability) + ? (resourceId + "/" + capability) : capability.toString()) + .build()) + .put("node", new MapBuilder().putOpt(NAME, node).putOpt(ID, node == null ? null + : (resourceId + "/" + node)).buildOpt()) + .put("relationship", ((Map) req.getValue()).get("relationship")) + // renderEntry((Map.Entry)requirement, "occurrences", + // "node", "capability", "relationship") + .build(); + }).collect(Collectors.toList())); + + return this; + } + + public TypeAction withCapabilities() { + this.doCapabilities = true; + return this; + } + + TypeAction doCapabilities(org.onap.sdc.dcae.checker.Catalog theCatalog) { + if (!this.doCapabilities) { + return this; + } + + ctx.setValue("capabilities", + stream(theCatalog + .facets(Construct.Node, Facet.capabilities, + this.name)) + .map((Map.Entry capability) -> new MapBuilder() + .put(NAME, capability.getKey()).put("type", + new MapBuilder() + .put(NAME, ((Map) capability.getValue()) + .get("type")) + .put(ID, + resourceId + "/" + + ((Map) capability.getValue()) + .get("type")) + .build()) + .put(OCCURRENCES, + ((Map) capability.getValue()).get(OCCURRENCES)) + .putOpt("validSourceTypes", + ((Map) capability.getValue()).get("validSourceTypes")) + .build() + // renderEntry((Map.Entry)capability, + // "occurrences", + // "validSourceTypes") + ).collect(Collectors.toList())); + return this; + } + + public Future<Type> execute() { + org.onap.sdc.dcae.checker.Catalog catalog = ASDCCatalog.this.catalogs.get(this.resourceId); + if (catalog == null) { + return Futures.failedFuture(new Exception("No catalog available for resource " + this.resourceId + + ". You might want to fetch the model first.")); + } + + if (!catalog.hasType(Construct.Node, this.name)) { + return Futures.failedFuture( + new Exception("No " + this.name + " type in catalog for resource " + this.resourceId)); + } + + this.ctx = JXPathContext + .newContext(new MapBuilder().put(NAME, this.name).put(ID, this.resourceId + "/" + this.name) + .put(ITEM_ID, this.resourceId + "/" + this.name).build()); + + this.doHierarchy(catalog).doRequirements(catalog).doCapabilities(catalog); + + JSONObject pack = new JSONObject((Map) this.ctx.getContextBean()); + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), pack.toString(2)); + + return Futures.succeededFuture(proxies.build((Map) ctx.getContextBean(), Type.class)); + } + } + + public static interface Resource extends Catalog.Item<Resource> { + + @Override + @Proxy.DataMap(map = "uuid") + public String id(); + + public UUID uuid(); + + public UUID invariantUUID(); + + public String category(); + + public String subCategory(); + + public String lastUpdaterFullName(); + + public String version(); + + @Proxy.DataMap(proxy = true, elementType = Artifact.class) + public Artifacts artifacts(); + + } + + public static class Resources extends Elements<Resource> { + } + + public static interface Artifact extends Catalog.Element<Artifact> { + + @Proxy.DataMap(map = ARTIFACT_NAME) + public String name(); + + @Proxy.DataMap(map = "artifactType") + public String type(); + + @Proxy.DataMap(map = "artifactDescription") + public String description(); + + @Proxy.DataMap(map = "artifactUUID") + public UUID uuid(); + + @Proxy.DataMap(map = "artifactVersion") + public int version(); + + } + + public static class Artifacts extends Elements<Artifact> { + } + + public class ASDCLocator implements TargetLocator { + + private JSONArray artifacts; + private org.onap.sdc.dcae.checker.Catalog catalog; + + private ASDCLocator(JSONArray theArtifacts, org.onap.sdc.dcae.checker.Catalog theCatalog) { + this.artifacts = theArtifacts; + this.catalog = theCatalog; + } + + public boolean addSearchPath(URI theURI) { + return false; + } + + public boolean addSearchPath(String thePath) { + return false; + } + + public Iterable<URI> searchPaths() { + return Collections.emptySet(); + } + + public Target resolve(String theName) { + JSONObject targetArtifact = null; + + for (int i = 0; i < this.artifacts.length(); i++) { + JSONObject artifact = this.artifacts.getJSONObject(i); + String artifactName = artifact.getString(ARTIFACT_NAME); + if (StringUtils.containsIgnoreCase(artifactName, theName)) { + targetArtifact = artifact; + } + } + + if (targetArtifact == null) { + return null; + } + + ASDCTarget target = null; + if (this.catalog != null) { + // this is the caching!! + target = (ASDCTarget) this.catalog.getTarget(ASDCCatalog.this.getArtifactURI(targetArtifact)); + if (target != null && target.getVersion().equals(ASDCCatalog.this.getArtifactVersion(targetArtifact))) { + return target; + } + } + + return new ASDCTarget(targetArtifact); + } + } + + public class ASDCTarget extends Target { + + private String content; + private JSONObject artifact; + + private ASDCTarget(JSONObject theArtifact) { + super(ASDCCatalog.this.getArtifactName(theArtifact), ASDCCatalog.this.getArtifactURI(theArtifact)); + this.artifact = theArtifact; + } + + // here is a chance for caching within the catalog! Do not go fetch the + // artifact if it has not been changed since the + // last fetch. + + @Override + public Reader open() throws IOException { + if (this.content == null) { + try { + this.content = ASDCCatalog.this.asdc + .fetch(ASDCCatalog.this.getArtifactURL(this.artifact), String.class).waitForResult(); + } catch (Exception x) { + throw new IOException("Failed to load " + ASDCCatalog.this.getArtifactURL(this.artifact), x); + } + } + + // should return immediately a reader blocked until content + // available .. hard to handle errors + return new StringReader(this.content); + } + + public String getVersion() { + return ASDCCatalog.this.getArtifactVersion(this.artifact); + } + + } + + public static void main(String[] theArgs) throws Exception { + + ASDCCatalog catalog = new ASDCCatalog(new URI(theArgs[0])); + + Folder f = catalog.folder(theArgs[1]).withItems().withItemModels().execute().waitForResult(); + + debugLogger.log(LogLevel.DEBUG, ASDCCatalog.class.getName(), "folder: {}", f.data()); + + Resources items = f.elements(ITEMS, Resources.class); + if (items != null) { + for (Resource item : items) { + debugLogger.log(LogLevel.DEBUG, ASDCCatalog.class.getName(), "\titem: {} : {}",item.name(), item.data()); + Templates templates = item.elements(MODELS, Templates.class); + if (templates != null) { + for (Template t : templates) { + Template ft = catalog.template(t.id()).withNodes().withNodeProperties() + .withNodePropertiesAssignments().execute().waitForResult(); + + debugLogger.log(LogLevel.DEBUG, ASDCCatalog.class.getName(), "template data: {}", ft.data()); + } + } + } + } + } + +} diff --git a/dcaedt_catalog/api/src/main/resources/log4j.properties b/dcaedt_catalog/api/src/main/resources/log4j.properties new file mode 100644 index 0000000..6e159e5 --- /dev/null +++ b/dcaedt_catalog/api/src/main/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootLogger=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%c] %m%n + +log4j.logger.org.apache.http=DEBUG +log4j.logger.org.apache.http.wire=ERROR
\ No newline at end of file diff --git a/dcaedt_catalog/api/src/test/java/org/onap/sdc/dcae/catalog/ASDCCatalogTest.java b/dcaedt_catalog/api/src/test/java/org/onap/sdc/dcae/catalog/ASDCCatalogTest.java new file mode 100644 index 0000000..fcd92f0 --- /dev/null +++ b/dcaedt_catalog/api/src/test/java/org/onap/sdc/dcae/catalog/ASDCCatalogTest.java @@ -0,0 +1,88 @@ +package org.onap.sdc.dcae.catalog; + +import static org.assertj.core.api.Assertions.*; + +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.UUID; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.onap.sdc.dcae.catalog.asdc.ASDCCatalog; +import org.onap.sdc.dcae.catalog.asdc.ASDCCatalog.FolderAction; +import org.onap.sdc.dcae.catalog.asdc.ASDCCatalog.Resource; + +import static org.mockito.Mockito.*; + + +public class ASDCCatalogTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private static FolderAction getTarget() { + ASDCCatalog catalog = mock(ASDCCatalog.class); + when(catalog.folder("test")).thenCallRealMethod(); + FolderAction target = catalog.folder("test"); + return target; + } + + @Test + public void filterLatestVersion_null_throwIllegalArgumentException() { + // arrange + FolderAction target = getTarget(); + // assert + thrown.expect(IllegalArgumentException.class); + // act + target.filterLatestVersion(null); + } + + @Test + public void filterLatestVersion_emptyItemsList_emptyItemsList() throws URISyntaxException { + // arrange + FolderAction target = getTarget(); + // act + Collection<Resource> result = target.filterLatestVersion(new ArrayList<>()); + // assert + assertThat(result).isEmpty(); + } + + @Test + public void filterLatestVersion_itemWithTwoVersions_itemWithLatestVersion() { + // arrange + FolderAction target = getTarget(); + + UUID invariantUUID = UUID.randomUUID(); + Resource r1v1 = mock(Resource.class); + Resource r1v2 = mock(Resource.class); + when(r1v1.invariantUUID()).thenReturn(invariantUUID); + when(r1v2.invariantUUID()).thenReturn(invariantUUID); + when(r1v1.version()).thenReturn("1.0"); + when(r1v2.version()).thenReturn("2.0"); + ArrayList<Resource> listItemWithTwoVersions = new ArrayList<Resource>(Arrays.asList(r1v1, r1v2)); + // act + Collection<Resource> result = target.filterLatestVersion(listItemWithTwoVersions); + // assert + assertThat(result).containsExactly(r1v2); + } + + @Test + public void filterLatestVersion_2distinctItems_2distinctItems() { + // arrange + FolderAction target = getTarget(); + + Resource r1 = mock(Resource.class); + Resource r2 = mock(Resource.class); + when(r1.invariantUUID()).thenReturn(UUID.randomUUID()); + when(r2.invariantUUID()).thenReturn(UUID.randomUUID()); + ArrayList<Resource> listOfTwoDistinctItems = new ArrayList<Resource>(Arrays.asList(r1, r2)); + // act + Collection<Resource> result = target.filterLatestVersion(listOfTwoDistinctItems); + // assert + assertThat(result).containsExactlyInAnyOrder(r1, r2); + } + +} |