aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorShwetank Dave <shwetank.dave@amdocs.com>2017-08-02 17:10:38 -0400
committerShwetank Dave <shwetank.dave@amdocs.com>2017-08-03 11:28:39 -0400
commite9c94429a8ada3e55854515060df817945b73d87 (patch)
tree3c5a9ac766d99570d542c1996f1321c99e5498e5 /src
parent27ff75c77611a8bdae37c7c0092787938c63a786 (diff)
[AAI-26] Adding gizmo data to the repository.
Change-Id: I183f837d45acbfe3c673fde1acf8768d5e3fd37b Signed-off-by: Shwetank Dave <shwetank.dave@amdocs.com>
Diffstat (limited to 'src')
-rw-r--r--src/main/ajsc/crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy11
-rw-r--r--src/main/ajsc/crud-api_v1/crud-api/v1/docs/README.txt1
-rw-r--r--src/main/ajsc/crud-api_v1/crud-api/v1/lib/README.txt1
-rw-r--r--src/main/ajsc/crud-api_v1/crud-api/v1/props/module.props1
-rw-r--r--src/main/ajsc/crud-api_v1/crud-api/v1/routes/crud.route4
-rw-r--r--src/main/ajsc/crud-api_v1/crud-api/v1/routes/helloWorld.route4
-rw-r--r--src/main/ajsc/crud-api_v1/crud-api/v1/routes/jaxrsExample.route5
-rw-r--r--src/main/assemble/ajsc_module_assembly.xml69
-rw-r--r--src/main/assemble/ajsc_props_assembly.xml26
-rw-r--r--src/main/assemble/ajsc_runtime_assembly.xml47
-rw-r--r--src/main/bin/start.sh54
-rw-r--r--src/main/config/ajsc-chef.jksbin0 -> 5256 bytes
-rw-r--r--src/main/config/ajsc-jetty.xml114
-rw-r--r--src/main/config/ajsc-override-web.xml53
-rw-r--r--src/main/config/ajscJetty.jksbin0 -> 3736 bytes
-rw-r--r--src/main/config/cadi.properties36
-rw-r--r--src/main/config/jul-redirect.properties13
-rw-r--r--src/main/config/keyfile27
-rw-r--r--src/main/config/runner-web.xml97
-rw-r--r--src/main/docker/Dockerfile26
-rw-r--r--src/main/java/org/openecomp/crud/dao/GraphDao.java138
-rw-r--r--src/main/java/org/openecomp/crud/dao/champ/ChampDao.java750
-rw-r--r--src/main/java/org/openecomp/crud/entity/Edge.java130
-rw-r--r--src/main/java/org/openecomp/crud/entity/Vertex.java102
-rw-r--r--src/main/java/org/openecomp/crud/event/GraphEvent.java235
-rw-r--r--src/main/java/org/openecomp/crud/event/GraphEventEdge.java212
-rw-r--r--src/main/java/org/openecomp/crud/event/GraphEventVertex.java181
-rw-r--r--src/main/java/org/openecomp/crud/exception/CrudException.java62
-rw-r--r--src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java128
-rw-r--r--src/main/java/org/openecomp/crud/logging/LoggingUtil.java88
-rw-r--r--src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java185
-rw-r--r--src/main/java/org/openecomp/crud/service/CrudGraphDataService.java209
-rw-r--r--src/main/java/org/openecomp/crud/service/CrudRestService.java686
-rw-r--r--src/main/java/org/openecomp/crud/service/EdgePayload.java115
-rw-r--r--src/main/java/org/openecomp/crud/service/JaxrsEchoService.java63
-rw-r--r--src/main/java/org/openecomp/crud/service/VertexPayload.java117
-rw-r--r--src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java92
-rw-r--r--src/main/java/org/openecomp/crud/util/CrudProperties.java77
-rw-r--r--src/main/java/org/openecomp/crud/util/CrudServiceConstants.java53
-rw-r--r--src/main/java/org/openecomp/crud/util/CrudServiceUtil.java56
-rw-r--r--src/main/java/org/openecomp/crud/util/FileWatcher.java48
-rw-r--r--src/main/java/org/openecomp/schema/OxmModelLoader.java168
-rw-r--r--src/main/java/org/openecomp/schema/OxmModelValidator.java325
-rw-r--r--src/main/java/org/openecomp/schema/RelationshipSchema.java138
-rw-r--r--src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java158
-rw-r--r--src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java341
-rw-r--r--src/main/resources/logging/CrudServiceMsgs.properties75
-rw-r--r--src/main/runtime/context/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.context1
-rw-r--r--src/main/runtime/context/default#0.context1
-rw-r--r--src/main/runtime/deploymentPackage/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.json1
-rw-r--r--src/main/runtime/shiroRole/ajscadmin.json1
-rw-r--r--src/main/runtime/shiroRole/contextadmin#__module.ajsc.namespace.name__.json1
-rw-r--r--src/main/runtime/shiroRole/contextadmin#default.json1
-rw-r--r--src/main/runtime/shiroUser/ajsc.json1
-rw-r--r--src/main/runtime/shiroUserRole/ajsc#ajscadmin.json1
-rw-r--r--src/main/runtime/shiroUserRole/ajsc#contextadmin#__module.ajsc.namespace.name__.json1
-rw-r--r--src/main/runtime/shiroUserRole/ajsc#contextadmin#default.json1
-rw-r--r--src/test/java/org/openecomp/crud/dao/champ/ChampDaoTest.java624
-rw-r--r--src/test/java/org/openecomp/schema/OxmModelLoaderTest.java22
-rw-r--r--src/test/resources/gremlin/gremlinResponseEmptyVertex.json15
-rw-r--r--src/test/resources/gremlin/gremlinResponseMultipleVertex.json108
-rw-r--r--src/test/resources/gremlin/gremlinResponseVertex.json76
62 files changed, 6376 insertions, 0 deletions
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy b/src/main/ajsc/crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy
new file mode 100644
index 0000000..8462e3e
--- /dev/null
+++ b/src/main/ajsc/crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy
@@ -0,0 +1,11 @@
+beans{
+ xmlns cxf: "http://camel.apache.org/schema/cxf"
+ xmlns jaxrs: "http://cxf.apache.org/jaxrs"
+ xmlns util: "http://www.springframework.org/schema/util"
+
+ echoService(org.openecomp.crud.service.JaxrsEchoService)
+
+ util.list(id: 'jaxrsServices') {
+ ref(bean:'echoService')
+ }
+} \ No newline at end of file
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/docs/README.txt b/src/main/ajsc/crud-api_v1/crud-api/v1/docs/README.txt
new file mode 100644
index 0000000..3707179
--- /dev/null
+++ b/src/main/ajsc/crud-api_v1/crud-api/v1/docs/README.txt
@@ -0,0 +1 @@
+Place any docs here that you want to access within the ajsc upon deployment of your service.
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/lib/README.txt b/src/main/ajsc/crud-api_v1/crud-api/v1/lib/README.txt
new file mode 100644
index 0000000..639e21b
--- /dev/null
+++ b/src/main/ajsc/crud-api_v1/crud-api/v1/lib/README.txt
@@ -0,0 +1 @@
+3rd party JAR's needed by your jars (if any) for a ajsc deployment package go here... \ No newline at end of file
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/props/module.props b/src/main/ajsc/crud-api_v1/crud-api/v1/props/module.props
new file mode 100644
index 0000000..17ebc08
--- /dev/null
+++ b/src/main/ajsc/crud-api_v1/crud-api/v1/props/module.props
@@ -0,0 +1 @@
+EXAMPLE.PROPERTY=EXAMLE_VALUE \ No newline at end of file
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/routes/crud.route b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/crud.route
new file mode 100644
index 0000000..185aa6a
--- /dev/null
+++ b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/crud.route
@@ -0,0 +1,4 @@
+<route xmlns="http://camel.apache.org/schema/spring" trace="true">
+ <from uri="att-dme2-servlet:///inventory?matchOnUriPrefix=true" />
+ <to uri="cxfbean:crudServices" />
+</route> \ No newline at end of file
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/routes/helloWorld.route b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/helloWorld.route
new file mode 100644
index 0000000..bc3e178
--- /dev/null
+++ b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/helloWorld.route
@@ -0,0 +1,4 @@
+<route xmlns="http://camel.apache.org/schema/spring" trace="true">
+ <from uri="restlet:/__module_ajsc_namespace_name__/__module_ajsc_namespace_version__/helloWorld"/>
+ <to uri="bean:helloWorld?method=speak"/>
+</route> \ No newline at end of file
diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/routes/jaxrsExample.route b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/jaxrsExample.route
new file mode 100644
index 0000000..5158e4f
--- /dev/null
+++ b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/jaxrsExample.route
@@ -0,0 +1,5 @@
+<route xmlns="http://camel.apache.org/schema/spring" trace="true">
+ <from uri="att-dme2-servlet:///__module_ajsc_namespace_name__/__module_ajsc_namespace_version__/echo-service/?matchOnUriPrefix=true" />
+ <to uri="cxfbean:jaxrsServices" />
+</route>
+
diff --git a/src/main/assemble/ajsc_module_assembly.xml b/src/main/assemble/ajsc_module_assembly.xml
new file mode 100644
index 0000000..359f792
--- /dev/null
+++ b/src/main/assemble/ajsc_module_assembly.xml
@@ -0,0 +1,69 @@
+<!--
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<assembly
+ xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+ <id>${version}</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>zip</format>
+ </formats>
+ <fileSets>
+ <fileSet>
+ <directory>${project.basedir}/target/versioned-ajsc/routes/</directory>
+ <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/routes/</outputDirectory>
+ <includes>
+ <include>*.route</include>
+ </includes>
+
+ </fileSet>
+
+ <fileSet>
+ <directory>${project.basedir}/target/versioned-ajsc/docs/</directory>
+ <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/docs/</outputDirectory>
+ <includes>
+ <include>*.*</include>
+ <!-- <include>*.vm</include> -->
+ </includes>
+
+ </fileSet>
+
+ <fileSet>
+ <directory>${project.basedir}/target/versioned-ajsc/lib/</directory>
+ <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/lib/</outputDirectory>
+ <includes>
+ <include>*.jar</include>
+ </includes>
+
+ </fileSet>
+ <fileSet>
+ <directory>${project.basedir}/target/versioned-ajsc/extJars/</directory>
+ <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/extJars/</outputDirectory>
+ <includes>
+ <include>*.jar</include>
+ </includes>
+ </fileSet>
+
+ <!-- also try to grab outputs from the "jar" plugin's package phase -->
+ <fileSet>
+ <directory>${project.basedir}/target/</directory>
+ <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/lib/</outputDirectory>
+ <includes>
+ <include>*.jar</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>${project.basedir}/target/versioned-ajsc/conf/</directory>
+ <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/conf/</outputDirectory>
+ <includes>
+ <include>*.*</include>
+ </includes>
+
+ </fileSet>
+ </fileSets>
+
+</assembly>
+
diff --git a/src/main/assemble/ajsc_props_assembly.xml b/src/main/assemble/ajsc_props_assembly.xml
new file mode 100644
index 0000000..6ee4093
--- /dev/null
+++ b/src/main/assemble/ajsc_props_assembly.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<assembly
+ xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+ <id>${version}_properties</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>zip</format>
+ </formats>
+ <fileSets>
+ <fileSet>
+ <directory>${project.basedir}/target/versioned-ajsc/props</directory>
+ <outputDirectory>${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/props/</outputDirectory>
+ <includes>
+ <include>*.props</include>
+ </includes>
+
+ </fileSet>
+
+ </fileSets>
+
+</assembly>
+
diff --git a/src/main/assemble/ajsc_runtime_assembly.xml b/src/main/assemble/ajsc_runtime_assembly.xml
new file mode 100644
index 0000000..c86d265
--- /dev/null
+++ b/src/main/assemble/ajsc_runtime_assembly.xml
@@ -0,0 +1,47 @@
+<!--
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<assembly
+ xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+ <id>runtimeEnvironment</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>zip</format>
+ </formats>
+ <fileSets>
+ <fileSet>
+ <directory>${project.basedir}/target/versioned-runtime/context/</directory>
+ <outputDirectory>runtime/context/</outputDirectory>
+ <includes>
+ <include>*.context</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>${project.basedir}/target/versioned-runtime/serviceProperties/</directory>
+ <outputDirectory>runtime/serviceProperties/</outputDirectory>
+ <includes>
+ <include>*.props</include>
+ </includes>
+ </fileSet><fileSet>
+ <directory>${project.basedir}/target/versioned-runtime/shiroRole</directory>
+ <outputDirectory>runtime/shiroRole/</outputDirectory>
+ <includes>
+ <include>*.json</include>
+ </includes>
+ </fileSet><fileSet>
+ <directory>${project.basedir}/target/versioned-runtime/shiroUser</directory>
+ <outputDirectory>runtime/shiroUser/</outputDirectory>
+ <includes>
+ <include>*.json</include>
+ </includes>
+ </fileSet><fileSet>
+ <directory>${project.basedir}/target/versioned-runtime/shiroUserRole</directory>
+ <outputDirectory>runtime/shiroUserRole</outputDirectory>
+ <includes>
+ <include>*.json</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+</assembly> \ No newline at end of file
diff --git a/src/main/bin/start.sh b/src/main/bin/start.sh
new file mode 100644
index 0000000..8a68460
--- /dev/null
+++ b/src/main/bin/start.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+BASEDIR="/opt/app/crud-api/"
+AJSC_HOME="$BASEDIR"
+AJSC_CONF_HOME="$AJSC_HOME/bundleconfig/"
+
+if [ -z "$CONFIG_HOME" ]; then
+ echo "CONFIG_HOME must be set in order to start up process"
+ exit 1
+fi
+
+if [ -z "$KEY_STORE_PASSWORD" ]; then
+ echo "KEY_STORE_PASSWORD must be set in order to start up process"
+ exit 1
+else
+ echo -e "KEY_STORE_PASSWORD=$KEY_STORE_PASSWORD\n" >> $AJSC_CONF_HOME/etc/sysprops/sys-props.properties
+fi
+
+if [ -z "$KEY_MANAGER_PASSWORD" ]; then
+ echo "KEY_MANAGER_PASSWORD must be set in order to start up process"
+ exit 1
+else
+ echo -e "KEY_MANAGER_PASSWORD=$KEY_MANAGER_PASSWORD\n" >> $AJSC_CONF_HOME/etc/sysprops/sys-props.properties
+fi
+
+# Add any spring bean configuration files to the Gizmo deployment
+if [ -n "$SERVICE_BEANS" ]; then
+ echo "Adding the following dynamic service beans to the deployment: "
+ mkdir -p /tmp/crud-api/v1/conf
+ for f in `ls $SERVICE_BEANS`
+ do
+ cp $SERVICE_BEANS/$f /tmp/crud-api/v1/conf
+ echo "Adding dynamic service bean $SERVICE_BEANS/$f"
+ done
+ jar uf /opt/app/crud-api/services/crud-api_v1.zip* -C /tmp/ crud-api
+ rm -rf /tmp/crud-api
+fi
+
+CLASSPATH="$AJSC_HOME/lib/*"
+CLASSPATH="$CLASSPATH:$AJSC_HOME/extJars/"
+CLASSPATH="$CLASSPATH:$AJSC_HOME/etc/"
+PROPS="-DAJSC_HOME=$AJSC_HOME"
+PROPS="$PROPS -DAJSC_CONF_HOME=$BASEDIR/bundleconfig/"
+PROPS="$PROPS -Dlogback.configurationFile=$BASEDIR/bundleconfig/etc/logback.xml"
+PROPS="$PROPS -DAJSC_SHARED_CONFIG=$AJSC_CONF_HOME"
+PROPS="$PROPS -DAJSC_SERVICE_NAMESPACE=crud-api"
+PROPS="$PROPS -DAJSC_SERVICE_VERSION=v1"
+PROPS="$PROPS -Dserver.port=9520"
+PROPS="$PROPS -DCONFIG_HOME=$CONFIG_HOME"
+JVM_MAX_HEAP=${MAX_HEAP:-1024}
+
+echo $CLASSPATH
+
+exec java -Xmx${JVM_MAX_HEAP}m $PROPS -classpath $CLASSPATH com.att.ajsc.runner.Runner context=// sslport=9520
diff --git a/src/main/config/ajsc-chef.jks b/src/main/config/ajsc-chef.jks
new file mode 100644
index 0000000..aeca770
--- /dev/null
+++ b/src/main/config/ajsc-chef.jks
Binary files differ
diff --git a/src/main/config/ajsc-jetty.xml b/src/main/config/ajsc-jetty.xml
new file mode 100644
index 0000000..17814f8
--- /dev/null
+++ b/src/main/config/ajsc-jetty.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<!-- Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. -->
+<Configure id="ajsc-server" class="org.eclipse.jetty.server.Server">
+ <!-- DO NOT REMOVE!!!! This is setting up the AJSC Context -->
+ <New id="ajscContext" class="org.eclipse.jetty.webapp.WebAppContext">
+ <Set name="contextPath"><SystemProperty name="AJSC_CONTEXT_PATH" /></Set>
+ <Set name="extractWAR">true</Set>
+ <Set name="tempDirectory"><SystemProperty name="AJSC_TEMP_DIR" /></Set>
+ <Set name="war"><SystemProperty name="AJSC_WAR_PATH" /></Set>
+ <Set name="descriptor"><SystemProperty name="AJSC_HOME" />/etc/runner-web.xml</Set>
+ <Set name="overrideDescriptor"><SystemProperty name="AJSC_HOME" />/etc/ajsc-override-web.xml</Set>
+ <Set name="throwUnavailableOnStartupException">true</Set>
+ <Set name="servletHandler">
+ <New class="org.eclipse.jetty.servlet.ServletHandler">
+ <Set name="startWithUnavailable">false</Set>
+ </New>
+ </Set>
+ <Set name="extraClasspath"><SystemProperty name="AJSC_HOME" />/extJars/json-20131018.jar</Set>
+ </New>
+
+ <Set name="handler">
+ <New id="Contexts"
+ class="org.eclipse.jetty.server.handler.ContextHandlerCollection">
+ <Set name="Handlers">
+ <Array type="org.eclipse.jetty.webapp.WebAppContext">
+ <Item>
+ <Ref refid="ajscContext" />
+ </Item>
+ </Array>
+ </Set>
+ </New>
+ </Set>
+
+ <Call name="addBean">
+ <Arg>
+ <New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
+ <Set name="contexts">
+ <Ref refid="Contexts" />
+ </Set>
+ <Call id="extAppHotDeployProvider" name="addAppProvider">
+ <Arg>
+ <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
+ <Set name="monitoredDirName"><SystemProperty name="AJSC_HOME" />/extApps</Set>
+ <Set name="scanInterval">10</Set>
+ <Set name="extractWars">true</Set>
+ </New>
+ </Arg>
+ </Call>
+ </New>
+ </Arg>
+ </Call>
+
+ <New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
+ <Set name="keyStorePath">file:<SystemProperty name="CONFIG_HOME" />/auth/tomcat_keystore</Set>
+ <Set name="KeyStorePassword">
+ <Call class="org.eclipse.jetty.util.security.Password" name="deobfuscate">
+ <Arg><SystemProperty name="KEY_STORE_PASSWORD" /></Arg>
+ </Call>
+ </Set>
+ <Set name="KeyManagerPassword">
+ <Call class="org.eclipse.jetty.util.security.Password" name="deobfuscate">
+ <Arg><SystemProperty name="KEY_MANAGER_PASSWORD" /></Arg>
+ </Call>
+ </Set>
+ <Set name="needClientAuth">true</Set>
+ <Set name="wantClientAuth">true</Set>
+ </New>
+
+ <Call id="sslConnector" name="addConnector">
+ <Arg>
+ <New class="org.eclipse.jetty.server.ServerConnector">
+ <Arg name="server">
+ <Ref refid="ajsc-server" />
+ </Arg>
+ <Arg name="factories">
+ <Array type="org.eclipse.jetty.server.ConnectionFactory">
+ <Item>
+ <New class="org.eclipse.jetty.server.SslConnectionFactory">
+ <Arg name="next">http/1.1</Arg>
+ <Arg name="sslContextFactory">
+ <Ref refid="sslContextFactory" />
+ </Arg>
+ </New>
+ </Item>
+ <Item>
+ <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+ <Arg name="config">
+ <New class="org.eclipse.jetty.server.HttpConfiguration">
+ <Call name="addCustomizer">
+ <Arg>
+ <New class="org.eclipse.jetty.server.SecureRequestCustomizer" />
+ </Arg>
+ </Call>
+ </New>
+ </Arg>
+ </New>
+ </Item>
+ </Array>
+ </Arg>
+ <Set name="port"><SystemProperty name="AJSC_HTTPS_PORT" default="9520" /></Set>
+ <Set name="idleTimeout">30000</Set>
+ </New>
+ </Arg>
+ </Call>
+
+ <Get name="ThreadPool">
+ <Set name="minThreads"><SystemProperty name="AJSC_JETTY_ThreadCount_MIN" /></Set>
+ <Set name="maxThreads"><SystemProperty name="AJSC_JETTY_ThreadCount_MAX" /></Set>
+ <Set name="idleTimeout"><SystemProperty name="AJSC_JETTY_IDLETIME_MAX" /></Set>
+ <Set name="detailedDump">false</Set>
+ </Get>
+
+</Configure>
diff --git a/src/main/config/ajsc-override-web.xml b/src/main/config/ajsc-override-web.xml
new file mode 100644
index 0000000..84a7920
--- /dev/null
+++ b/src/main/config/ajsc-override-web.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ metadata-complete="false" version="3.0">
+
+ <filter-mapping>
+ <filter-name>InterceptorFilter</filter-name>
+ <url-pattern>/services/*</url-pattern>
+ </filter-mapping>
+ <filter-mapping>
+ <filter-name>InterceptorFilter</filter-name>
+ <url-pattern>/rest/*</url-pattern>
+ </filter-mapping>
+
+ <filter-mapping>
+ <filter-name>springSecurityFilterChain</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+ <servlet-mapping>
+ <servlet-name>ManagementServlet</servlet-name>
+ <url-pattern>/mgmt</url-pattern>
+ </servlet-mapping>
+
+ <servlet-mapping>
+ <servlet-name>RestletServlet</servlet-name>
+ <url-pattern>/rest/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet-mapping>
+ <servlet-name>CamelServlet</servlet-name>
+ <url-pattern>/services/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet-mapping>
+ <servlet-name>jsp</servlet-name>
+ <url-pattern>*.jsp</url-pattern>
+ <url-pattern>*.jspf</url-pattern>
+ <url-pattern>*.jspx</url-pattern>
+ <url-pattern>*.xsp</url-pattern>
+ <url-pattern>*.JSP</url-pattern>
+ <url-pattern>*.JSPF</url-pattern>
+ <url-pattern>*.JSPX</url-pattern>
+ <url-pattern>*.XSP</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>default</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+</web-app> \ No newline at end of file
diff --git a/src/main/config/ajscJetty.jks b/src/main/config/ajscJetty.jks
new file mode 100644
index 0000000..48cdbff
--- /dev/null
+++ b/src/main/config/ajscJetty.jks
Binary files differ
diff --git a/src/main/config/cadi.properties b/src/main/config/cadi.properties
new file mode 100644
index 0000000..4720e1c
--- /dev/null
+++ b/src/main/config/cadi.properties
@@ -0,0 +1,36 @@
+#This properties file is used for defining AAF properties related to the CADI framework. This file is used for running AAF framework
+
+#In order to test functionality of cadi-ajsc-plugin locally cross domain cookie. Cadi "should" find your hostname for you.
+#However, we have seen some situations where this fails. A Local testing
+#modification can include modifying your hosts file so that you can use "mywebserver.att.com" for your localhost in order
+#to test/verify GLO functionality locally. If you are on a Windows machine, you will already have a machine name associated with
+#it that will utilize an AT&T domain such as "sbc.com". You may need to add your domain to this as a comma separated list depending
+#upon your particular machine domain. This property is commented out as cadi SHOULD find your machine name. With version 1.2.1 of cadi,
+#it appears to resolve Mac machine names as well, now. But, this can be somewhat inconsistent depending on your specific working envrironment.
+hostname=mywebserver.att.com
+
+#Setting csp_domain to PROD will allow for testing using your attuid and password through GLO.
+csp_domain=PROD
+csp_devl_localhost=true
+
+basic_realm=csp.att.com
+#basic_realm=aaf.att.com
+basic_warn=TRUE
+
+cadi_loglevel=WARN
+cadi_keyfile=target/swm/package/nix/dist_files/appl/crud-api/etc/keyfile
+
+# Configure AAF
+#These are dummy values add appropriate values required
+aaf_url=url
+
+#AJSC - MECHID
+#These are dummy values add appropriate values required
+aaf_id=dummyid@ajsc.att.com
+aaf_password=enc:277edqJCjT0RlUI3BtbDQa-3Ha-CQGd
+aaf_timeout=5000
+aaf_clean_interval=30000
+aaf_user_expires=5000
+aaf_high_count=1000
+
+
diff --git a/src/main/config/jul-redirect.properties b/src/main/config/jul-redirect.properties
new file mode 100644
index 0000000..8b6624d
--- /dev/null
+++ b/src/main/config/jul-redirect.properties
@@ -0,0 +1,13 @@
+
+# Bridge JUL->slf4j Logging Configuration File
+#
+# This file bridges the JUL logging infrastructure into
+# SLF4J so JUL logs go to logback implementation provided
+# in this project. SLF4J also captures log4j and has
+# other framework options as well providing a common
+# logging infrastructure for capturing all logs from different
+# libraries using different frameworks in one place.
+
+# Global properties
+handlers=org.slf4j.bridge.SLF4JBridgeHandler
+.level= ALL
diff --git a/src/main/config/keyfile b/src/main/config/keyfile
new file mode 100644
index 0000000..882e86a
--- /dev/null
+++ b/src/main/config/keyfile
@@ -0,0 +1,27 @@
+ZuIwp0TkyVPDeX1Up-8JtkMWvjsCpoiu1_VKeWrtrvxunvAke8_tiFyHPPyb2nkhepFYj6tXzpfS
+rGz5XF_TH9NbsKaP8u0HV5clz2WriYQRvHS85vjY7hXxkpFuLb7zkLAPqTyIDpj7FiW61NzsRUAq
+TM8jH16jr7mBNnb56w24mNGOwznMPcIZKcjgZU1ekaPDFpWyhQElU7Y0q_94P_Gkk45r66Hj22sU
+OiOaaftmudZlswLw8-8Zaakqf2yW9HjMVfuYCwSodBHCW5rdB3Ctb5W36rnD_AQco3Ky2PgPmqvk
+QkJYuUHpbuDqVHqLOajlKSIGMTIqAIBg51fRaaONtD-Q5xzY8E5wO1YWTLKcP5tsNvUpzM8Wu3NS
+ynpGpUcvlTqWWsGzTbzOyamyKkdNdx97sSqjM25Zh1-ps48h6cddGYWpab7SUvqRCS11QBUyLTry
+2iwTEHMhHRIbo7PO99ALQfuq9gI1zKGfurJdvLBeBaFs5SCF0AiCZ3WcDO8Rv3HpxVZ2_ShbDxb0
+eMoO6SotXu51fj8Y3-WqsfZziQyEsHyqpg5uQ6yUtz01h5YHLEoVuotF1U4agmQR6kEkYk-wNOiZ
+v-8gaA9gtbLoAdKhuKFxQgQLNMf6GzVzZNujbmDzLoZAP_mXAv29aBPaf64Ugzv-Oa5GZdBgD-Xd
+_pahML-ionw99r0TnkpShYmDqMKhMdjaP3m87WIAZkIB-L-VTyKcEsJ4340VSzCOsv3waiM0S89u
+4cMcG5y-PLY8IoipIlLUPTWD3SjcQ9DV1Dt3T5KjdWLsj48D3W4K4e9PB8yxs0gtUjgVUR2_xEir
+G5eDO9Ac1eHFWGDFFP0SgG-TbHJUKlvy9mwLzmU0fC3xPjhqmIr-v0HxF7HN-tmb1LHDorno8tSN
+u7kUGcKSchIiFfvkd066crUb2mH7PnXTaWmAjyVj9VsBExFUYEdpHMAV4sAP9-RxZGDRt46UhrDK
+QZvvNhBVyOEjHPHWI4vl1r1v8HNH1_2jZu5DVJWyHWR56aCo1lhFH9_X6UAHUHbnXViDONZOVXlT
+9-WD0tk2zJGuwrhdZDAnPnAmjfwbwbpnr5Hmex1i1JiD7WVyP1kbfoej2TmdiYbxr9oBYaGQ29JI
+aHod7MQCLtvL1z5XgnDPLZ4y3_9SbqHKYbNa8UgZkTLF5EacGThYVFDLA9cbafHDtR1kMGE3vv4D
+EJ-0pAYTOGmKlVI7DwNyKsY9JTyudrxTqhOxi9jgcJNWiUaNe9yhL8Pyc2YBqUTTYhh_a2d1rvkZ
+0Gh1crviVxqBrIkRKaMRXZ4f1vDLz-3NvG_vwPOo8WRFo5nGmSdTw7CjBaigJ_cYCfDhoP11pEnw
+cndsZNcHs-v05LlxeIIMDD_f5Bvz-il_DLA4eK2HqgLdxh8ziSDl2azk14MJY4amzz6reEXUuKLV
+RsZGf_jbDGKhE2HuDQ5ovoLOi4OqE1oRuqh-dGxitrYouP2SN1l_1tCEMRth86FMV-6AQtZsvdUo
+y9MtQ7e35atjA8nHtgADlDTmJBKQiUHUsOZ77p1qp17HAFMovUkc739opfEYnKUn6Itpw5Ipm_Is
+ra6chJUfMpOFof5rb5OjqFAN27c_-mPo1lQU3ndYlKGh_n5V8ufX6v2Yri8WzOPf6hjVYotkmoMP
+NPAICDCB8W5ddBjsopzLVVEtaXDu9Qj6-zf77hT4iQ7rBd2Ner8iLqN3Kis0dvkNM3_uH8onau1G
+Y_YYw7PPSZyd2S_7Dd6G-IG4ayO6e5DD6oUwwekyiQI_3rTXNa_wldGxqW9u818010ekE4Qdlfcj
+beIn7fAeaOjReZ87hRgWyMs-EgTVHw8RL3yI_O6VvRTVRONRF1Y4C_-IYa8z-bfrwXx3BBd9TTgb
+EnS9wVOyC2OgUN6BhPLGLhxzkJ05nEjizXEc9t5EPYoSRwesajGGrrG_0-qWbuU5hKLPLkyeJLHb
+5HXOTVsrUR59Vov2M3_EswkxcImblox3k3VS2yihZMGyfqLzZIUXgd8ufkevKKU6DxwacGTb \ No newline at end of file
diff --git a/src/main/config/runner-web.xml b/src/main/config/runner-web.xml
new file mode 100644
index 0000000..b51aff4
--- /dev/null
+++ b/src/main/config/runner-web.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ metadata-complete="false" version="3.0">
+
+ <context-param>
+ <param-name>contextConfigLocation</param-name>
+ <param-value>/WEB-INF/spring-servlet.xml,
+ classpath:applicationContext.xml
+ </param-value>
+ </context-param>
+
+ <context-param>
+ <param-name>spring.profiles.default</param-name>
+ <param-value>nooauth</param-value>
+ </context-param>
+
+ <listener>
+ <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+ </listener>
+
+ <servlet>
+ <servlet-name>ManagementServlet</servlet-name>
+ <servlet-class>ajsc.ManagementServlet</servlet-class>
+ </servlet>
+
+
+ <filter>
+ <filter-name>InterceptorFilter</filter-name>
+ <filter-class>ajsc.filters.InterceptorFilter</filter-class>
+ <init-param>
+ <param-name>preProcessor_interceptor_config_file</param-name>
+ <param-value>/etc/PreProcessorInterceptors.properties</param-value>
+ </init-param>
+ <init-param>
+ <param-name>postProcessor_interceptor_config_file</param-name>
+ <param-value>/etc/PostProcessorInterceptors.properties</param-value>
+ </init-param>
+
+ </filter>
+
+ <servlet>
+ <servlet-name>RestletServlet</servlet-name>
+ <servlet-class>ajsc.restlet.RestletSpringServlet</servlet-class>
+ <init-param>
+ <param-name>org.restlet.component</param-name>
+ <param-value>restletComponent</param-value>
+ </init-param>
+ </servlet>
+
+ <servlet>
+ <servlet-name>CamelServlet</servlet-name>
+ <servlet-class>ajsc.servlet.AjscCamelServlet</servlet-class>
+ </servlet>
+
+
+ <filter>
+ <filter-name>springSecurityFilterChain</filter-name>
+ <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
+ </filter>
+
+ <servlet>
+ <servlet-name>spring</servlet-name>
+ <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+<!-- <servlet-mapping>
+ <servlet-name>spring</servlet-name>
+ <url-pattern>/</url-pattern>
+ </servlet-mapping>-->
+
+<!-- BEGIN jsp -->
+
+ <servlet id="jsp">
+ <servlet-name>jsp</servlet-name>
+ <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+ </servlet>
+
+
+
+
+
+ <!-- BEGIN static content -->
+ <servlet>
+ <servlet-name>default</servlet-name>
+ <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
+ <init-param>
+ <param-name>dirAllowed</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ </servlet>
+ <!-- END static content -->
+</web-app>
diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile
new file mode 100644
index 0000000..e4459d1
--- /dev/null
+++ b/src/main/docker/Dockerfile
@@ -0,0 +1,26 @@
+FROM ubuntu:14.04
+
+ARG MICRO_HOME=/opt/app/crud-api
+ARG BIN_HOME=$MICRO_HOME/bin
+
+RUN apt-get update
+
+# Install and setup java8
+RUN apt-get update && apt-get install -y software-properties-common
+## sudo -E is required to preserve the environment. If you remove that line, it will most like freeze at this step
+RUN sudo -E add-apt-repository ppa:openjdk-r/ppa && apt-get update && apt-get install -y openjdk-8-jdk
+## Setup JAVA_HOME, this is useful for docker commandline
+ENV JAVA_HOME usr/lib/jvm/java-8-openjdk-amd64
+RUN export JAVA_HOME
+
+# Build up the deployment folder structure
+RUN mkdir -p $MICRO_HOME
+ADD swm/package/nix/dist_files/appl/crud-api/* $MICRO_HOME/
+RUN mkdir -p $BIN_HOME
+COPY *.sh $BIN_HOME
+RUN chmod 755 $BIN_HOME/*
+RUN ln -s /logs $MICRO_HOME/logs
+
+EXPOSE 9520 9520
+
+CMD ["/opt/app/crud-api/bin/start.sh"]
diff --git a/src/main/java/org/openecomp/crud/dao/GraphDao.java b/src/main/java/org/openecomp/crud/dao/GraphDao.java
new file mode 100644
index 0000000..20b568c
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/dao/GraphDao.java
@@ -0,0 +1,138 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.dao;
+
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.List;
+import java.util.Map;
+
+public interface GraphDao {
+
+ public Vertex getVertex(String id, String type) throws CrudException;
+
+ /**
+ * Retrieve all of the edges which are incident to the vertex with the specified identifier.
+ *
+ * @param id - The unique identifier of the vertex to retrieve the edges for.
+ * @return - A collection of edges.
+ * @throws CrudException
+ */
+ public List<Edge> getVertexEdges(String id) throws CrudException;
+
+ /**
+ * Retrieve a collection of {@link Vertex} objects which match the supplied type label
+ * and filter properties.
+ *
+ * @param type - The vertex type that we want to retrieve.
+ * @param filter - The parameters to filter our results by.
+ * @return - A collection of vertices.
+ * @throws CrudException
+ */
+ public List<Vertex> getVertices(String type, Map<String, Object> filter) throws CrudException;
+
+ /**
+ * Retrieve an {@link Edge} from the graph database by specifying its unique identifier.
+ *
+ * @param id - The unique identifier for the Edge to be retrieved.
+ * @return - The Edge corresponding to the specified identifier.
+ * @throws CrudException
+ */
+ public Edge getEdge(String id, String type) throws CrudException;
+
+ /**
+ * Retrieve a collection of {@link Edge} objects with a given type and which match a set of
+ * supplied filter parameters.
+ *
+ * @param type - The type of edges that we are interested in.
+ * @param filter - The parameters that we want to filter our edges by.
+ * @return - A collection of edges which match the supplied filter parameters.
+ * @throws CrudException
+ */
+ public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException;
+
+ /**
+ * Insert a new {@link Vertex} into the graph data store.
+ *
+ * @param type - The type label to assign to the vertex.
+ * @param properties - The properties to associated with this vertex.
+ * @return - The {@link Vertex} object that was created.
+ * @throws CrudException
+ */
+ public Vertex addVertex(String type, Map<String, Object> properties) throws CrudException;
+
+ /**
+ * Updates an existing {@link Vertex}.
+ *
+ * @param id - The unique identifier of the vertex to be updated.
+ * @param properties - The properties to associate with the vertex.
+ * @return - The udpated vertex.
+ * @throws CrudException
+ */
+ public Vertex updateVertex(String id, String type, Map<String, Object> properties)
+ throws CrudException;
+
+ /**
+ * Removes the specified vertex from the graph data base.
+ *
+ * <p>NOTE: The vertex MUST contain NO incident edges before it can be deleted.
+ *
+ * @param id - The unique identifier of the vertex to be deleted.
+ * @throws CrudException
+ */
+ public void deleteVertex(String id, String type) throws CrudException;
+
+ /**
+ * Adds an edge to the graph database.
+ *
+ * @param type - The 'type' label to apply to the edge.
+ * @param source - The source vertex for this edge.
+ * @param target - The target vertex for this edge.
+ * @param properties - The properties map to associate with this edge.
+ * @return - The {@link Edge} object that was created.
+ * @throws CrudException
+ */
+ public Edge addEdge(String type, Vertex source, Vertex target, Map<String, Object> properties)
+ throws CrudException;
+
+ /**
+ * Updates an existing {@link Edge}.
+ *
+ * @param id - The unique identifier of the edge to be updated.
+ * @param properties - The properties to associate with the edge.
+ * @return - The update edge.
+ * @throws CrudException
+ */
+ public Edge updateEdge(Edge edge) throws CrudException;
+
+ /**
+ * Remove the specified edge from the graph data base.
+ *
+ * @param id - The unique identifier of the edge to be deleted.
+ * @throws CrudException
+ */
+ public void deleteEdge(String id, String type) throws CrudException;
+}
diff --git a/src/main/java/org/openecomp/crud/dao/champ/ChampDao.java b/src/main/java/org/openecomp/crud/dao/champ/ChampDao.java
new file mode 100644
index 0000000..ff0a332
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/dao/champ/ChampDao.java
@@ -0,0 +1,750 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.dao.champ;
+
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.object.ObjectBuildOrPropertiesStep;
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.dao.GraphDao;
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.logging.CrudServiceMsgs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+
+/**
+ * This is the integration layer between the CRUD API service and the low level Champ library
+ * for graph database interaction.
+ */
+public class ChampDao implements GraphDao {
+
+ public static final String CONFIG_STORAGE_BACKEND = "storage.backend";
+ public static final String CONFIG_STORAGE_BACKEND_DB = "storage.backend.db";
+ public static final String STORAGE_HBASE_DB = "hbase";
+ public static final String STORAGE_CASSANDRA_DB = "cassandra";
+ public static final String CONFIG_STORAGE_HOSTNAMES = "storage.hostnames";
+ public static final String CONFIG_STORAGE_PORT = "storage.port";
+ public static final String CONFIG_HBASE_ZNODE_PARENT = "storage.hbase.ext.zookeeper.znode.parent";
+ public static final String CONFIG_GRAPH_NAME = "graph.name";
+ public static final String GRAPH_UNQ_INSTANCE_ID_SUFFIX = "graph.unique-instance-id-suffix";
+
+ public static final String CONFIG_EVENT_STREAM_PUBLISHER = "event.stream.publisher";
+ public static final String CONFIG_EVENT_STREAM_NUM_PUBLISHERS = "event.stream.num-publishers";
+
+
+ public static final String DEFAULT_GRAPH_NAME = "default_graph";
+
+ /**
+ * Set of configuration properties for the DAI.
+ */
+ private Properties daoConfig;
+
+ /**
+ * Instance of the API used for interacting with the Champ library.
+ */
+ private ChampGraph champApi = null;
+
+ private Logger logger = LoggerFactory.getInstance().getLogger(ChampDao.class.getName());
+
+
+ /**
+ * Creates a new instance of the ChampDao.
+ *
+ * @param config - Set of configuration properties to be applied to this instance
+ * of the DAO.
+ */
+ public ChampDao(Properties config) {
+
+ // Store the configuration properties.
+ daoConfig = config;
+
+ // Apply the configuration to the DAO.
+ configure();
+
+ }
+
+
+ @Override
+ public Vertex getVertex(String id, String type) throws CrudException {
+
+ try {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("getVertex with id: " + id);
+ }
+
+ long idAsLong = Long.parseLong(id);
+
+ // Request the vertex from the graph db.
+ Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
+
+ // Did we find it?
+ if (retrievedVertex.isPresent() && retrievedVertex.get().getProperties()
+ .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName())!=null && retrievedVertex.get().getProperties()
+ .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()
+ .equalsIgnoreCase(type)) {
+
+ // Yup, convert it to a Vector object and return it.
+ return vertexFromChampObject(retrievedVertex.get(),type);
+
+ } else {
+
+ // We didn't find a vertex with the supplied id, so just throw an
+ // exception.
+ throw new CrudException("No vertex with id " + id + " found in graph",
+ javax.ws.rs.core.Response.Status.NOT_FOUND);
+ }
+
+ } catch (ChampUnmarshallingException e) {
+
+ // Something went wrong - throw an exception.
+ throw new CrudException(e.getMessage(),
+ javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+
+ @Override
+ public List<Edge> getVertexEdges(String id) throws CrudException {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("get Edges incident to vertex with id: " + id + " from graph");
+ }
+
+ try {
+ long idAsLong = Long.parseLong(id); // GDF - what to do about id???
+
+ // Request the vertex from the graph db.
+ Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong);
+
+ // Did we find it?
+ if (retrievedVertex.isPresent()) {
+
+ // Query the Champ library for the edges which are incident to the specified
+ // vertex.
+ Stream<ChampRelationship> relationships =
+ champApi.retrieveRelationships(retrievedVertex.get());
+
+ // Build an edge list from the result stream.
+ List<Edge> edges = new ArrayList<Edge>();
+ relationships.forEach(r -> edges.add(edgeFromChampRelationship(r)));
+
+ return edges;
+
+ } else {
+
+ // We couldn't find the specified vertex, so throw an exception.
+ throw new CrudException("No vertex with id " + id + " found in graph",
+ javax.ws.rs.core.Response.Status.NOT_FOUND);
+ }
+
+ } catch (ChampUnmarshallingException e) {
+
+ // Something went wrong, so throw an exception.
+ throw new CrudException(e.getMessage(),
+ javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+
+ } catch (ChampObjectNotExistsException e) {
+
+ // We couldn't find the specified vertex, so throw an exception.
+ throw new CrudException("No vertex with id " + id + " found in graph",
+ javax.ws.rs.core.Response.Status.NOT_FOUND);
+ }
+ }
+
+
+ @Override
+ public Vertex addVertex(String type, Map<String, Object> properties) throws CrudException {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Add/update vertex: {label: " + type
+ + " properties:" + propertiesMapToString(properties));
+ }
+
+ //Add the aai_node_type so that AAI can read the data created by gizmo
+ properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
+
+ // Create an object to represent our vertex in the format expected by the Champ library.
+ ChampObject objectToCreate = buildChampObject(type, properties);
+
+ try {
+
+ // Ask the Champ library to store our vertex, placing the returned object into a
+ // list so that we can easily put that into our result object.
+ return vertexFromChampObject(champApi.storeObject(objectToCreate),type);
+
+ } catch (ChampMarshallingException
+ | ChampSchemaViolationException
+ | ChampObjectNotExistsException e) {
+
+ // Something went wrong - throw an exception.
+ throw new CrudException(e.getMessage(),
+ javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+
+ @Override
+ public Vertex updateVertex(String id, String type, Map<String, Object> properties)
+ throws CrudException {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Update vertex with id: " + id + " with properties: "
+ + propertiesMapToString(properties));
+ }
+ //Add the aai_node_type so that AAI can read the data created by gizmo
+ properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
+
+ try {
+ // Now, build the updated version of the Champ Object...
+ ChampObject updateObject = buildChampObject(id, type, properties);
+ // ...and send it to the Champ library.
+ return vertexFromChampObject(champApi.replaceObject(updateObject),type);
+
+ } catch (ChampObjectNotExistsException e) {
+ throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
+ } catch (NumberFormatException | ChampMarshallingException | ChampSchemaViolationException e) {
+ throw new CrudException(e.getMessage(),
+ javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+ }
+
+ }
+
+
+ @Override
+ public List<Vertex> getVertices(String type, Map<String, Object> filter) throws CrudException {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Retrieve vertices with type label: " + type + " which map query parameters: "
+ + propertiesMapToString(filter));
+ }
+
+
+ filter.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
+
+
+ Stream<ChampObject> retrievedVertices = champApi.queryObjects(filter);
+
+ List<Vertex> vertices = retrievedVertices
+ .map(v -> vertexFromChampObject(v,type))
+ .collect(Collectors.toList());
+
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Resulting vertex list: " + retrievedVertices);
+ }
+
+ // ...and return it to the caller.
+ return vertices;
+ }
+
+ private Object getRelKey(String id) {
+ Object key = id;
+ // convert into Long if applicable . TODO : revisit in story NUC-304
+ try {
+ key = Long.parseLong(id);
+ } catch (NumberFormatException e) {
+ // The id isn't a Long, leave it as a string
+ }
+
+ return key;
+ }
+
+ @Override
+ public Edge getEdge(String id, String type) throws CrudException {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Get edge with id: " + id);
+ }
+
+ try {
+
+ // Request the edge from the graph db.
+ Optional<ChampRelationship> relationship = champApi.retrieveRelationship(getRelKey(id));
+
+ // Did we find it?
+ if (relationship.isPresent() && relationship.get().getType().equals(type)) {
+
+ // Yup - return the result.
+ return edgeFromChampRelationship(relationship.get());
+
+ } else {
+
+ // We didn't find an edge with the supplied id, so throw an exception.
+ throw new CrudException("No edge with id " + id + " found in graph",
+ javax.ws.rs.core.Response.Status.NOT_FOUND);
+ }
+
+ } catch (ChampUnmarshallingException e) {
+
+ // Something went wrong, so throw an exception.
+ throw new CrudException(e.getMessage(),
+ javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ public Edge addEdge(String type,
+ Vertex source,
+ Vertex target,
+ Map<String, Object> properties) throws CrudException {
+
+ // For now, assume source and target are straight ids...
+ try {
+
+ Optional<ChampObject> sourceObject
+ = champApi.retrieveObject(Long.parseLong(source.getId().get()));
+ if (!sourceObject.isPresent() || !sourceObject.get().getType().equals(source.getType())) {
+ throw new CrudException("Error creating edge - source vertex with id " + source
+ + " does not exist in graph data base",
+ javax.ws.rs.core.Response.Status.BAD_REQUEST);
+ }
+
+ Optional<ChampObject> targetObject
+ = champApi.retrieveObject(Long.parseLong(target.getId().get()));
+ if (!targetObject.isPresent() || !targetObject.get().getType().equals(target.getType())) {
+ throw new CrudException("Error creating edge - target vertex with id " + target
+ + " does not exist in graph data base",
+ javax.ws.rs.core.Response.Status.BAD_REQUEST);
+ }
+
+ // Now, create the ChampRelationship object for our edge and store it in
+ // the graph database.
+ return edgeFromChampRelationship(
+ champApi.storeRelationship(
+ new ChampRelationship.Builder(sourceObject.get(), targetObject.get(), type)
+ .properties(properties)
+ .build()));
+
+ } catch (ChampMarshallingException
+ | ChampObjectNotExistsException
+ | ChampSchemaViolationException
+ | ChampRelationshipNotExistsException
+ | ChampUnmarshallingException e) {
+
+ throw new CrudException("Error creating edge: " + e.getMessage(),
+ javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+
+ @Override
+ public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException {
+
+ filter.put(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), type);
+
+ Stream<ChampRelationship> retrievedRelationships = champApi.queryRelationships(filter);
+ // Process the result stream from the Champ library into an Edge list, keeping only
+ // edges of the specified type.
+ List<Edge> edges = retrievedRelationships
+ .map(r -> edgeFromChampRelationship(r))
+ .collect(Collectors.toList());
+
+ return edges;
+ }
+
+ @Override
+ public Edge updateEdge(Edge edge) throws CrudException {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Update edge with id: " + edge.getId() + " with properties: "
+ + propertiesMapToString(edge.getProperties()));
+ }
+
+ try {
+ // Now, build the updated version of the Champ Relationship...
+ ChampRelationship updateRelationship = new ChampRelationship.Builder(
+ buildChampObject(edge.getSource().getId().get(), edge.getSource().getType(),
+ edge.getSource().getProperties()),
+ buildChampObject(edge.getTarget().getId().get(), edge.getTarget().getType(),
+ edge.getTarget().getProperties()),
+ edge.getType()).key(getRelKey(edge.getId().get()))
+ .properties(edge.getProperties()).build();
+ // ...and send it to the Champ library.
+ return edgeFromChampRelationship(champApi.replaceRelationship(updateRelationship));
+
+
+ } catch (ChampRelationshipNotExistsException ex) {
+ throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND);
+ } catch (NumberFormatException | ChampUnmarshallingException | ChampMarshallingException
+ | ChampSchemaViolationException ex) {
+
+ throw new CrudException(ex.getMessage(),
+ javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ public void deleteVertex(String id, String type) throws CrudException {
+
+ try {
+
+ // First, retrieve the vertex that we intend to delete.
+ Optional<ChampObject> retrievedVertex = champApi.retrieveObject(Long.parseLong(id));
+
+ // Did we find it?
+ if (!retrievedVertex.isPresent() || !retrievedVertex.get().getType().equals(type)) {
+ throw new CrudException("Failed to delete vertex with id: "
+ + id + " - vertex does not exist.",
+ javax.ws.rs.core.Response.Status.NOT_FOUND);
+ }
+
+ // Now, verify that there are no edges incident to the vertex (they must be deleted
+ // first if so).
+ Stream<ChampRelationship> relationships =
+ champApi.retrieveRelationships(retrievedVertex.get());
+
+ if (relationships.count() > 0) {
+ throw new CrudException("Attempt to delete vertex with id "
+ + id + " which has incident edges.",
+ javax.ws.rs.core.Response.Status.BAD_REQUEST);
+ }
+
+ // Finally, we can attempt to delete our vertex.
+ champApi.deleteObject(Long.parseLong(id));
+
+
+ } catch (NumberFormatException
+ | ChampUnmarshallingException
+ | ChampObjectNotExistsException e) {
+
+ throw new CrudException(e.getMessage(),
+ javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ public void deleteEdge(String id, String type) throws CrudException {
+
+ try {
+
+ // First, retrieve the edge that we want to delete.
+ Optional<ChampRelationship> relationshipToDelete
+ = champApi.retrieveRelationship(getRelKey(id));
+
+
+ // Did we find it?
+ if (!relationshipToDelete.isPresent() || !relationshipToDelete.get().getType().equals(type)) {
+ throw new CrudException("Failed to delete edge with id: " + id + " - edge does not exist",
+ javax.ws.rs.core.Response.Status.NOT_FOUND);
+ }
+
+ // Now we can delete the edge.
+ champApi.deleteRelationship(relationshipToDelete.get());
+
+ } catch (ChampRelationshipNotExistsException
+ | NumberFormatException
+ | ChampUnmarshallingException e) {
+
+ throw new CrudException(e.getMessage(),
+ javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+
+ /**
+ * This helper method generates a string representation of a properties map for
+ * logging purposes.
+ *
+ * @param properties - The properties map to be converted.
+ * @return - The log statement friendly conversion of the properties map.
+ */
+ private String propertiesMapToString(Map<String, Object> properties) {
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+
+ for (String key : properties.keySet()) {
+ sb.append("(").append(key).append(" -> ").append(properties.get(key)).append(") ");
+ }
+
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+
+ /**
+ * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library.
+ *
+ * @param type - The type to assign to our ChampObject
+ * @param properties - The set of properties to assign to our ChampObject
+ * @return - A populated ChampObject
+ */
+ private ChampObject buildChampObject(String type, Map<String, Object> properties) {
+
+ ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create()
+ .ofType(type)
+ .withoutKey();
+
+ for (String key : properties.keySet()) {
+ objectInProgress.withProperty(key, properties.get(key));
+ }
+ return objectInProgress.build();
+ }
+
+
+ /**
+ * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library.
+ *
+ * @param id - Unique identifier for this object.
+ * @param type - The type to assign to our ChampObject
+ * @param properties - The set of properties to assign to our ChampObject
+ * @return - A populated ChampObject
+ */
+ private ChampObject buildChampObject(String id, String type, Map<String, Object> properties) {
+
+ ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create()
+ .ofType(type)
+ .withKey(Long.parseLong(id));
+
+ for (String key : properties.keySet()) {
+ objectInProgress.withProperty(key, properties.get(key));
+ }
+ return objectInProgress.build();
+ }
+
+
+
+
+ private Vertex vertexFromChampObject(ChampObject champObject, String type) {
+
+ // Get the identifier for this vertex from the Champ object.
+ Object id = champObject.getKey().orElse("");
+
+ // Start building our {@link Vertex} object.
+ Vertex.Builder vertexBuilder = new Vertex.Builder(type);
+ vertexBuilder.id(id.toString());
+
+ // Convert the properties associated with the Champ object into the form expected for
+ // a Vertex object.
+ for (String key : champObject.getProperties().keySet()) {
+ vertexBuilder.property(key, champObject.getProperties().get(key));
+ }
+
+ // ...and return it.
+ return vertexBuilder.build();
+ }
+
+
+ /**
+ * This helper method converts a {@link ChampRelationship} from the Champ library into an
+ * equivalent {@link Edge} object that is understood by the CRUD Service.
+ *
+ * @param relationship - The ChampRelationship object to be converted.
+ * @return - An Edge object corresponding to the supplied ChampRelationship
+ */
+ private Edge edgeFromChampRelationship(ChampRelationship relationship) {
+
+ // Populate the edge's id, if available.
+ Object relationshipId = relationship.getKey().orElse("");
+
+ Edge.Builder edgeBuilder = new Edge.Builder(relationship.getType())
+ .id(relationshipId.toString());
+ edgeBuilder.source(vertexFromChampObject(relationship.getSource(),
+ relationship.getSource().getProperties()
+ .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
+ ? relationship.getSource().getType()
+ : relationship.getSource().getProperties()
+ .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
+ edgeBuilder.target(vertexFromChampObject(relationship.getTarget(),
+ relationship.getTarget().getProperties()
+ .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null
+ ? relationship.getTarget().getType()
+ : relationship.getTarget().getProperties()
+ .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString()));
+
+ for (String key : relationship.getProperties().keySet()) {
+ edgeBuilder.property(key, relationship.getProperties().get(key).toString());
+ }
+
+ return edgeBuilder.build();
+ }
+
+
+ /**
+ * Performs all one-time configuration operations which are required when creating
+ * a new instance of the DAO.
+ */
+ private void configure() {
+
+ // Instantiate the Champ library API.
+ try {
+
+ // Determine which back end we are using.
+ switch (getBackendTypeFromConfig()) {
+
+ case IN_MEMORY:
+
+ logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO,
+ "In Memory",
+ daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME),
+ "Not applicable");
+
+ champApi = ChampGraph.Factory.newInstance(ChampGraph.Type.IN_MEMORY,
+ daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME));
+
+ break;
+
+ case TITAN:
+ try {
+ String db = daoConfig.getProperty(CONFIG_STORAGE_BACKEND_DB);
+ Short graphIdSuffix = (short) new Random().nextInt(Short.MAX_VALUE);
+ logger.info(CrudServiceMsgs.TITAN_GRAPH_INFO, GRAPH_UNQ_INSTANCE_ID_SUFFIX
+ + ": = " + graphIdSuffix);
+ if (db.equalsIgnoreCase(STORAGE_CASSANDRA_DB)) {
+ logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, "Titan with cassandra backend",
+ daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME),
+ daoConfig.getProperty(CONFIG_STORAGE_HOSTNAMES));
+
+ TitanChampGraphImpl.Builder champApiBuilder =
+ new TitanChampGraphImpl.Builder(daoConfig.getProperty(CONFIG_GRAPH_NAME,
+ DEFAULT_GRAPH_NAME))
+ .property("storage.backend", "cassandrathrift")
+ .property(GRAPH_UNQ_INSTANCE_ID_SUFFIX, graphIdSuffix)
+ .property("storage.hostname", daoConfig.get(CONFIG_STORAGE_HOSTNAMES));
+
+ if (daoConfig.containsKey(CONFIG_EVENT_STREAM_PUBLISHER)) {
+ champApiBuilder.property("champ.event.stream.publisher",
+ daoConfig.get(CONFIG_EVENT_STREAM_PUBLISHER));
+ }
+
+ if (daoConfig.containsKey(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)) {
+ champApiBuilder.property("champ.event.stream.publisher-pool-size",
+ daoConfig.get(CONFIG_EVENT_STREAM_NUM_PUBLISHERS));
+ }
+
+ champApi = champApiBuilder.build();
+
+ } else if (db.equalsIgnoreCase(STORAGE_HBASE_DB)) {
+
+ logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, "Titan with Hbase backend",
+ daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME),
+ daoConfig.getProperty(CONFIG_STORAGE_HOSTNAMES));
+ TitanChampGraphImpl.Builder champApiBuilder =
+ new TitanChampGraphImpl.Builder(daoConfig
+ .getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME))
+ .property("storage.backend", "hbase")
+ .property("storage.hbase.ext.zookeeper.znode.parent",
+ daoConfig.get(CONFIG_HBASE_ZNODE_PARENT))
+ .property("storage.port", daoConfig.get(CONFIG_STORAGE_PORT))
+ .property(GRAPH_UNQ_INSTANCE_ID_SUFFIX, graphIdSuffix)
+ .property("storage.hostname", daoConfig.get(CONFIG_STORAGE_HOSTNAMES));
+
+ if (daoConfig.containsKey(CONFIG_EVENT_STREAM_PUBLISHER)) {
+ champApiBuilder.property("champ.event.stream.publisher",
+ daoConfig.get(CONFIG_EVENT_STREAM_PUBLISHER));
+ }
+
+ if (daoConfig.containsKey(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)) {
+ champApiBuilder.property("champ.event.stream.publisher-pool-size",
+ daoConfig.get(CONFIG_EVENT_STREAM_NUM_PUBLISHERS));
+ }
+ champApi = champApiBuilder.build();
+ } else {
+ logger.error(CrudServiceMsgs.INVALID_GRAPH_BACKEND,
+ daoConfig.getProperty(CONFIG_STORAGE_BACKEND_DB));
+ }
+
+ } catch (com.thinkaurelius.titan.core.TitanException e) {
+
+ logger.error(CrudServiceMsgs.INSTANTIATE_GRAPH_BACKEND_ERR, "Titan", e.getMessage());
+ }
+
+
+ break;
+
+ default:
+ logger.error(CrudServiceMsgs.INVALID_GRAPH_BACKEND,
+ daoConfig.getProperty(CONFIG_STORAGE_BACKEND));
+ break;
+ }
+
+ } catch (CrudException e) {
+ logger.error(CrudServiceMsgs.INSTANTIATE_GRAPH_BACKEND_ERR,
+ daoConfig.getProperty(CONFIG_STORAGE_BACKEND), e.getMessage());
+ }
+ }
+
+
+ /**
+ * Performs any necessary shut down operations when the DAO is no longer needed.
+ */
+ public void close() {
+
+ if (champApi != null) {
+
+ logger.info(CrudServiceMsgs.STOPPING_CHAMP_DAO);
+
+ champApi.shutdown();
+ }
+ }
+
+
+ /**
+ * This helper function converts the 'graph back end type' config parameter into the
+ * corresponding {@link ChampAPI.Type}.
+ *
+ * @return - A {@link ChampAPI.Type}
+ * @throws CrudException
+ */
+ private ChampGraph.Type getBackendTypeFromConfig() throws CrudException {
+
+ // Get the back end type from the DAO's configuration properties.
+ String backend = daoConfig.getProperty(CONFIG_STORAGE_BACKEND, "in-memory");
+
+ // Now, find the appropriate ChampAPI type and return it.
+ if (backend.equals("in-memory")) {
+ return ChampGraph.Type.IN_MEMORY;
+ } else if (backend.equals("titan")) {
+ return ChampGraph.Type.TITAN;
+ }
+
+ // If we are here, then whatever was in the config properties didn't match to a supported
+ // back end type, so just throw an exception and let the caller figure it out.
+ throw new CrudException("Invalid graph backend type '" + backend + "' specified.",
+ javax.ws.rs.core.Response.Status.BAD_REQUEST);
+ }
+
+
+}
diff --git a/src/main/java/org/openecomp/crud/entity/Edge.java b/src/main/java/org/openecomp/crud/entity/Edge.java
new file mode 100644
index 0000000..803511f
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/entity/Edge.java
@@ -0,0 +1,130 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.entity;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class Edge {
+ private static final Gson gson = new GsonBuilder().create();
+
+ private final Optional<String> id;
+ private final String type;
+ private final Map<String, Object> properties;
+ private final Vertex source;
+ private final Vertex target;
+
+
+ private Edge(Builder builder) {
+ this.id = builder.id;
+ this.type = builder.type;
+ this.source = builder.source;
+ this.target = builder.target;
+ this.properties = builder.properties;
+ }
+
+ public static class Builder {
+ private Optional<String> id = Optional.empty();
+ private final String type;
+ private Vertex source;
+ private Vertex target;
+ private final Map<String, Object> properties = new HashMap<String, Object>();
+
+ public Builder(String type) {
+ if (type == null) {
+ throw new IllegalArgumentException("Type cannot be null");
+ }
+
+ this.type = type;
+ }
+
+ public Builder id(String id) {
+ if (id == null) {
+ throw new IllegalArgumentException("id cannot be null");
+ }
+
+ this.id = Optional.of(id);
+ return this;
+ }
+
+ public Builder property(String key, Object value) {
+ if (key == null || value == null) {
+ throw new IllegalArgumentException("Property key/value cannot be null");
+ }
+ properties.put(key, value);
+ return this;
+ }
+
+ public Builder source(Vertex source) {
+ this.source = source;
+ return this;
+ }
+
+ public Builder target(Vertex target) {
+ this.target = target;
+ return this;
+ }
+
+ public Edge build() {
+ return new Edge(this);
+ }
+ }
+
+
+ @Override
+ public String toString() {
+ return "Edge [id=" + id + ", type=" + type + ", properties=" + properties
+ + ", source=" + source + ", target=" + target + "]";
+ }
+
+ public String toJson() {
+ return gson.toJson(this);
+ }
+
+ public Optional<String> getId() {
+ return id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ public Vertex getSource() {
+ return source;
+ }
+
+ public Vertex getTarget() {
+ return target;
+ }
+
+
+}
diff --git a/src/main/java/org/openecomp/crud/entity/Vertex.java b/src/main/java/org/openecomp/crud/entity/Vertex.java
new file mode 100644
index 0000000..1b0b97d
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/entity/Vertex.java
@@ -0,0 +1,102 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.entity;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class Vertex {
+ private static final Gson gson = new GsonBuilder().create();
+
+ private final Optional<String> id;
+ private final String type;
+ private final Map<String, Object> properties;
+
+
+ private Vertex(Builder builder) {
+ this.id = builder.id;
+ this.type = builder.type;
+ this.properties = builder.properties;
+ }
+
+ public static class Builder {
+ private Optional<String> id = Optional.empty();
+ private final String type;
+ private final Map<String, Object> properties = new HashMap<String, Object>();
+
+ public Builder(String type) {
+ if (type == null) {
+ throw new IllegalArgumentException("Type cannot be null");
+ }
+ this.type = type;
+ }
+
+ public Builder id(String id) {
+ if (id == null) {
+ throw new IllegalArgumentException("id cannot be null");
+ }
+
+ this.id = Optional.of(id);
+ return this;
+ }
+
+ public Builder property(String key, Object value) {
+ if (key == null || value == null) {
+ throw new IllegalArgumentException("Property key/value cannot be null");
+ }
+ properties.put(key, value);
+ return this;
+ }
+
+ public Vertex build() {
+ return new Vertex(this);
+ }
+ }
+
+ public String toJson() {
+ return gson.toJson(this);
+ }
+
+ @Override
+ public String toString() {
+ return "Vertex [id=" + id + ", type=" + type + ", properties=" + properties + "]";
+ }
+
+ public Optional<String> getId() {
+ return id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+}
diff --git a/src/main/java/org/openecomp/crud/event/GraphEvent.java b/src/main/java/org/openecomp/crud/event/GraphEvent.java
new file mode 100644
index 0000000..392bf2d
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/event/GraphEvent.java
@@ -0,0 +1,235 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.event;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.annotations.SerializedName;
+
+import org.openecomp.crud.exception.CrudException;
+
+import javax.ws.rs.core.Response.Status;
+
+public class GraphEvent {
+
+ public enum GraphEventOperation {
+ CREATE, UPDATE, DELETE
+ }
+
+ public enum GraphEventResult {
+ SUCCESS, FAILURE
+ }
+
+ private GraphEventOperation operation;
+
+ @SerializedName("transaction-id")
+ private String transactionId;
+
+ private long timestamp;
+
+ private GraphEventVertex vertex;
+
+ private GraphEventEdge edge;
+
+ private GraphEventResult result;
+
+ @SerializedName("error-message")
+ private String errorMessage;
+
+ private Status httpErrorStatus;
+
+ /**
+ * Marshaller/unmarshaller for converting to/from JSON.
+ */
+ private static final Gson gson = new GsonBuilder().disableHtmlEscaping()
+ .setPrettyPrinting().create();
+
+ public static Builder builder(GraphEventOperation operation) {
+ return new Builder(operation);
+ }
+
+ public GraphEventOperation getOperation() {
+ return operation;
+ }
+
+ public String getTransactionId() {
+ return transactionId;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public GraphEventVertex getVertex() {
+ return vertex;
+ }
+
+ public GraphEventEdge getEdge() {
+ return edge;
+ }
+
+ public GraphEventResult getResult() {
+ return result;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setResult(GraphEventResult result) {
+ this.result = result;
+ }
+
+
+ public Status getHttpErrorStatus() {
+ return httpErrorStatus;
+ }
+
+ public void setHttpErrorStatus(Status httpErrorStatus) {
+ this.httpErrorStatus = httpErrorStatus;
+ }
+
+ public void setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ public void setVertex(GraphEventVertex vertex) {
+ this.vertex = vertex;
+ }
+
+ public void setEdge(GraphEventEdge edge) {
+ this.edge = edge;
+ }
+
+ /**
+ * Unmarshalls this Vertex object into a JSON string.
+ *
+ * @return - A JSON format string representation of this Vertex.
+ */
+ public String toJson() {
+ return gson.toJson(this);
+ }
+
+ /**
+ * Marshalls the provided JSON string into a Vertex object.
+ *
+ * @param json - The JSON string to produce the Vertex from.
+ * @return - A Vertex object.
+ * @throws SpikeException
+ */
+ public static GraphEvent fromJson(String json) throws CrudException {
+
+ try {
+
+ // Make sure that we were actually provided a non-empty string
+ // before we
+ // go any further.
+ if (json == null || json.isEmpty()) {
+ throw new CrudException("Empty or null JSON string.", Status.BAD_REQUEST);
+ }
+
+ // Marshall the string into a Vertex object.
+ return gson.fromJson(json, GraphEvent.class);
+
+ } catch (Exception ex) {
+ throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST);
+ }
+ }
+
+ @Override
+ public String toString() {
+
+ return toJson();
+ }
+
+ public String getObjectKey() {
+ if (this.getVertex() != null) {
+ return this.getVertex().getId();
+ } else if (this.getEdge() != null) {
+ return this.getEdge().getId();
+ }
+
+ return null;
+
+ }
+
+ public String getObjectType() {
+ if (this.getVertex() != null) {
+ return "vertex->" + this.getVertex().getType();
+ } else if (this.getEdge() != null) {
+ return "edge->" + this.getEdge().getType();
+ }
+
+ return null;
+
+ }
+
+ public static class Builder {
+
+ GraphEvent event = null;
+
+ public Builder(GraphEventOperation operation) {
+ event = new GraphEvent();
+ event.operation = operation;
+ }
+
+ public Builder vertex(GraphEventVertex vertex) {
+ event.vertex = vertex;
+ return this;
+ }
+
+ public Builder edge(GraphEventEdge edge) {
+ event.edge = edge;
+ return this;
+ }
+
+ public Builder result(GraphEventResult result) {
+ event.result = result;
+ return this;
+ }
+
+ public Builder errorMessage(String errorMessage) {
+ event.errorMessage = errorMessage;
+ return this;
+ }
+
+ public Builder httpErrorStatus(Status httpErrorStatus) {
+ event.httpErrorStatus = httpErrorStatus;
+ return this;
+ }
+
+ public GraphEvent build() {
+
+ event.timestamp = System.currentTimeMillis();
+ event.transactionId = java.util.UUID.randomUUID().toString();
+
+ return event;
+ }
+ }
+
+}
diff --git a/src/main/java/org/openecomp/crud/event/GraphEventEdge.java b/src/main/java/org/openecomp/crud/event/GraphEventEdge.java
new file mode 100644
index 0000000..aaf9b72
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/event/GraphEventEdge.java
@@ -0,0 +1,212 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.event;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.annotations.SerializedName;
+import com.google.gson.reflect.TypeToken;
+
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.Map;
+import javax.ws.rs.core.Response.Status;
+
+/**
+ * This class provides a generic representation of an Edge as provided by the
+ * graph data store.
+ */
+public class GraphEventEdge {
+
+ /**
+ * The unique identifier used to identify this edge in the graph data store.
+ */
+ @SerializedName("key")
+ private String id;
+
+ @SerializedName("schema-version")
+ private String modelVersion;
+
+ /**
+ * Type label assigned to this vertex.
+ */
+ private String type;
+
+ /**
+ * Source vertex for our edge.
+ */
+ private GraphEventVertex source;
+
+ /**
+ * Target vertex for our edge.
+ */
+ private GraphEventVertex target;
+
+ /**
+ * Map of all of the properties assigned to this vertex.
+ */
+ private JsonElement properties;
+
+ /**
+ * Marshaller/unmarshaller for converting to/from JSON.
+ */
+ private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+
+ public GraphEventEdge(String id, String modelVersion, String type, GraphEventVertex source,
+ GraphEventVertex target, JsonElement properties) {
+ this.id = id;
+ this.modelVersion = modelVersion;
+ this.type = type;
+ this.source = source;
+ this.target = target;
+ this.properties = properties;
+ }
+
+ public GraphEventEdge() {
+
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public GraphEventVertex getSource() {
+ return source;
+ }
+
+ public void setSource(GraphEventVertex source) {
+ this.source = source;
+ }
+
+ public GraphEventVertex getTarget() {
+ return target;
+ }
+
+ public void setTarget(GraphEventVertex target) {
+ this.target = target;
+ }
+
+ public JsonElement getProperties() {
+ return properties;
+ }
+
+ public void setProperties(JsonElement properties) {
+ this.properties = properties;
+ }
+
+ public String getModelVersion() {
+ return modelVersion;
+ }
+
+ public void setModelVersion(String modelVersion) {
+ this.modelVersion = modelVersion;
+ }
+
+ /**
+ * Unmarshalls this Edge object into a JSON string.
+ *
+ * @return - A JSON format string representation of this Edge.
+ */
+ public String toJson() {
+ return gson.toJson(this);
+ }
+
+ /**
+ * Marshalls the provided JSON string into a Edge object.
+ *
+ * @param json - The JSON string to produce the Edge from.
+ * @return - A Edge object.
+ * @throws SpikeException
+ */
+ public static GraphEventEdge fromJson(String json) throws CrudException {
+
+ try {
+
+ // Make sure that we were actually provided a non-empty string
+ // before we
+ // go any further.
+ if (json == null || json.isEmpty()) {
+ throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST);
+ }
+
+ // Marshall the string into an Edge object.
+ return gson.fromJson(json, GraphEventEdge.class);
+
+ } catch (Exception ex) {
+ throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST);
+ }
+ }
+
+ public static GraphEventEdge fromEdge(Edge edge, String modelVersion) {
+
+ java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
+ JsonObject props = gson.toJsonTree(edge.getProperties(), mapType).getAsJsonObject();
+
+ GraphEventEdge graphEventEdge = new GraphEventEdge(edge.getId().orElse(""), modelVersion,
+ edge.getType(), new GraphEventVertex(edge.getSource().getId().orElse(""), null,
+ edge.getSource().getType(), null), new GraphEventVertex(edge.getTarget().getId().orElse(""),
+ null, edge.getTarget().getType(), null), props);
+
+ return graphEventEdge;
+
+ }
+
+ public Edge toEdge() {
+ Edge.Builder builder = new Edge.Builder(this.getType()).id(this.getId());
+ if (this.getSource() != null) {
+ builder.source(new Vertex.Builder(this.getSource().getType()).id(this.getSource().getId())
+ .build());
+ }
+ if (this.getTarget() != null) {
+ builder.target(new Vertex.Builder(this.getTarget().getType()).id(this.getTarget().getId())
+ .build());
+ }
+
+ if (this.getProperties() != null) {
+ java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
+ Map<String, Object> propertiesMap = gson.fromJson(this.getProperties(), mapType);
+ for (String key : propertiesMap.keySet()) {
+ builder.property(key, propertiesMap.get(key));
+ }
+ }
+ return builder.build();
+
+ }
+}
diff --git a/src/main/java/org/openecomp/crud/event/GraphEventVertex.java b/src/main/java/org/openecomp/crud/event/GraphEventVertex.java
new file mode 100644
index 0000000..7fde12a
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/event/GraphEventVertex.java
@@ -0,0 +1,181 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.event;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.annotations.SerializedName;
+import com.google.gson.reflect.TypeToken;
+
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.Map;
+import javax.ws.rs.core.Response.Status;
+
+/**
+ * This class provides a generic representation of a Vertex as provided by the
+ * graph data store.
+ */
+public class GraphEventVertex {
+
+ /**
+ * The unique identifier used to identify this vertex in the graph data
+ * store.
+ */
+ @SerializedName("key")
+ private String id;
+
+ @SerializedName("schema-version")
+ private String modelVersion;
+
+ /**
+ * Type label assigned to this vertex.
+ */
+ private String type;
+
+ /**
+ * Map of all of the properties assigned to this vertex.
+ */
+ private JsonElement properties;
+
+ /**
+ * Marshaller/unmarshaller for converting to/from JSON.
+ */
+ private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+
+ public GraphEventVertex(String id, String modelVersion, String type, JsonElement properties) {
+ this.id = id;
+ this.modelVersion = modelVersion;
+ this.type = type;
+ this.properties = properties;
+ }
+
+ public GraphEventVertex() {
+
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+
+ public JsonElement getProperties() {
+ return properties;
+ }
+
+ public void setProperties(JsonElement properties) {
+ this.properties = properties;
+ }
+
+ public String getModelVersion() {
+ return modelVersion;
+ }
+
+ public void setModelVersion(String modelVersion) {
+ this.modelVersion = modelVersion;
+ }
+
+ /**
+ * Unmarshalls this Vertex object into a JSON string.
+ *
+ * @return - A JSON format string representation of this Vertex.
+ */
+ public String toJson() {
+ return gson.toJson(this);
+ }
+
+ /**
+ * Marshalls the provided JSON string into a Vertex object.
+ *
+ * @param json - The JSON string to produce the Vertex from.
+ * @return - A Vertex object.
+ * @throws SpikeException
+ */
+ public static GraphEventVertex fromJson(String json) throws CrudException {
+
+ try {
+
+ // Make sure that we were actually provided a non-empty string
+ // before we
+ // go any further.
+ if (json == null || json.isEmpty()) {
+ throw new CrudException("Empty or null JSON string.", Status.BAD_REQUEST);
+ }
+
+ // Marshall the string into a Vertex object.
+ return gson.fromJson(json, GraphEventVertex.class);
+
+ } catch (Exception ex) {
+ throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST);
+ }
+ }
+
+ @Override
+ public String toString() {
+
+ return toJson();
+ }
+
+ public static GraphEventVertex fromVertex(Vertex vertex, String modelVersion) {
+
+ java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
+ JsonObject props = gson.toJsonTree(vertex.getProperties(), mapType).getAsJsonObject();
+ GraphEventVertex graphEventVertex = new GraphEventVertex(vertex.getId().orElse(""),
+ modelVersion, vertex.getType(), props);
+ return graphEventVertex;
+
+ }
+
+ public Vertex toVertex() {
+ Vertex.Builder builder = new Vertex.Builder(this.getType()).id(this.getId());
+
+ if (this.getProperties() != null) {
+ java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
+ Map<String, Object> propertiesMap = gson.fromJson(this.getProperties(), mapType);
+ for (String key : propertiesMap.keySet()) {
+ builder.property(key, propertiesMap.get(key));
+ }
+ }
+
+ return builder.build();
+
+ }
+
+
+}
diff --git a/src/main/java/org/openecomp/crud/exception/CrudException.java b/src/main/java/org/openecomp/crud/exception/CrudException.java
new file mode 100644
index 0000000..2911070
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/exception/CrudException.java
@@ -0,0 +1,62 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.exception;
+
+import javax.ws.rs.core.Response.Status;
+
+public class CrudException extends Exception {
+
+ private static final long serialVersionUID = 8162385108397238865L;
+
+ private Status httpStatus;
+
+ public CrudException() {
+ }
+
+ public CrudException(String message, Status httpStatus) {
+ super(message);
+ this.setHttpStatus(httpStatus);
+ }
+
+ public CrudException(Throwable cause) {
+ super(cause);
+ }
+
+ public CrudException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CrudException(String message, Throwable cause, boolean enableSuppression,
+ boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public Status getHttpStatus() {
+ return httpStatus;
+ }
+
+ public void setHttpStatus(Status httpStatus) {
+ this.httpStatus = httpStatus;
+ }
+}
diff --git a/src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java b/src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java
new file mode 100644
index 0000000..71fb14b
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java
@@ -0,0 +1,128 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.logging;
+
+import com.att.eelf.i18n.EELFResourceManager;
+
+import org.openecomp.cl.eelf.LogMessageEnum;
+
+public enum CrudServiceMsgs implements LogMessageEnum {
+
+ /**
+ * Received request {0} {1} from {2}. Sending response: {3}
+ *
+ * <p>Arguments:
+ * {0} = operation
+ * {1} = target URL
+ * {2} = source
+ * {3} = response code
+ */
+ PROCESS_REST_REQUEST,
+
+ INVALID_OXM_FILE,
+ INVALID_OXM_DIR,
+ OXM_FILE_CHANGED,
+
+ /**
+ * Successfully loaded schema: {0}
+ *
+ * <p>Arguments:
+ * {0} = oxm filename
+ */
+ LOADED_OXM_FILE,
+
+ /**
+ * Unable to load OXM schema: {0}
+ *
+ * <p>Arguments:
+ * {0} = error
+ */
+ OXM_LOAD_ERROR,
+
+ /**
+ * Instantiate data access layer for graph data store type: {0} graph: {1} using hosts: {2}
+ *
+ * <p>Arguments:
+ * {0} = Graph data store technology type
+ * {1} = Graph name
+ * {2} = Hosts list
+ */
+ INSTANTIATE_GRAPH_DAO,
+
+ /**
+ * Stopping ChampDAO...
+ *
+ * <p>Arguments:
+ */
+ STOPPING_CHAMP_DAO,
+
+ /**
+ * Unsupported graph database {0} specified.
+ *
+ * <p>Arguments:
+ * {0} = Graph database back end.
+ */
+ INVALID_GRAPH_BACKEND,
+
+ /**
+ * Failure instantiating {0} graph database backend. Cause: {1}
+ *
+ * <p>Arguments:
+ * {0} - Graph database type.
+ * {1} - Failure cause.
+ */
+ INSTANTIATE_GRAPH_BACKEND_ERR,
+
+ /**
+ * Failure instantiating CRUD Rest Service. Cause: {0}
+ *
+ * <p>Arguments:
+ * {0} - Failure cause.
+ */
+ INSTANTIATE_AUTH_ERR,
+
+ /**
+ * Any info log related to titan graph
+ *
+ * <p>Arguments:
+ * {0} - Info.
+ */
+ TITAN_GRAPH_INFO,
+
+ /**
+ * Arguments:
+ * {0} Opertaion
+ * {1} URI
+ * {2} = Exception
+ */
+ EXCEPTION_DURING_METHOD_CALL;
+
+
+ /**
+ * Static initializer to ensure the resource bundles for this class are loaded...
+ */
+ static {
+ EELFResourceManager.loadMessageBundle("logging/CrudServiceMsgs");
+ }
+}
diff --git a/src/main/java/org/openecomp/crud/logging/LoggingUtil.java b/src/main/java/org/openecomp/crud/logging/LoggingUtil.java
new file mode 100644
index 0000000..aaa250a
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/logging/LoggingUtil.java
@@ -0,0 +1,88 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.logging;
+
+import org.openecomp.cl.api.LogFields;
+import org.openecomp.cl.api.LogLine;
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.mdc.MdcContext;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.slf4j.MDC;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+
+public class LoggingUtil {
+ /**
+ * Initializes mdc context.
+ */
+ public static void initMdcContext(HttpServletRequest httpReq, HttpHeaders headers) {
+ String fromIp = httpReq.getRemoteAddr();
+ String fromAppId = "";
+ String transId = null;
+
+ if (headers.getRequestHeaders().getFirst("X-FromAppId") != null) {
+ fromAppId = headers.getRequestHeaders().getFirst("X-FromAppId");
+ }
+
+ if ((headers.getRequestHeaders().getFirst("X-TransactionId") == null)
+ || headers.getRequestHeaders().getFirst("X-TransactionId").isEmpty()) {
+ transId = java.util.UUID.randomUUID().toString();
+ } else {
+ transId = headers.getRequestHeaders().getFirst("X-TransactionId");
+ }
+
+ MdcContext.initialize(transId, CrudServiceConstants.CRD_SERVICE_NAME, "", fromAppId, fromIp);
+ }
+
+ /**
+ * Logs the rest request.
+ */
+ public static void logRestRequest(Logger logger, Logger auditLogger,
+ HttpServletRequest req, Response response) {
+ String respStatusString = "";
+ if (Response.Status.fromStatusCode(response.getStatus()) != null) {
+ respStatusString = Response.Status.fromStatusCode(response.getStatus()).toString();
+ }
+
+ // Generate error log
+ logger.info(CrudServiceMsgs.PROCESS_REST_REQUEST, req.getMethod(),
+ req.getRequestURL().toString(), req.getRemoteHost(),
+ Integer.toString(response.getStatus()));
+
+ // Generate audit log.
+ auditLogger.info(CrudServiceMsgs.PROCESS_REST_REQUEST,
+ new LogFields()
+ .setField(LogLine.DefinedFields.RESPONSE_CODE, response.getStatus())
+ .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, respStatusString),
+ (req != null) ? req.getMethod() : "Unknown",
+ (req != null) ? req.getRequestURL().toString() : "Unknown",
+ (req != null) ? req.getRemoteHost() : "Unknown",
+ Integer.toString(response.getStatus()) + " error: " + (response.getEntity() == null ? ""
+ : response.getEntity().toString()));
+ MDC.clear();
+ }
+}
diff --git a/src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java b/src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java
new file mode 100644
index 0000000..1a84322
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java
@@ -0,0 +1,185 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.parser;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.service.EdgePayload;
+import org.openecomp.crud.service.VertexPayload;
+import org.openecomp.schema.RelationshipSchemaLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CrudResponseBuilder {
+
+ private static final Gson gson = new GsonBuilder().create();
+
+ public static final String SOURCE = "source";
+ public static final String TARGET = "target";
+ public static final String URL_BASE = "services/inventory/";
+
+ public static String buildUpsertVertexResponse(Vertex vertex, String version)
+ throws CrudException {
+ VertexPayload payload = new VertexPayload();
+ payload.setId(vertex.getId().get());
+ payload.setType(vertex.getType());
+ payload.setUrl(URL_BASE + version + "/" + vertex.getType() + "/" + vertex.getId().get());
+ JsonObject props = new JsonObject();
+ for (String key : vertex.getProperties().keySet()) {
+ addJsonProperperty(props, key, vertex.getProperties().get(key));
+ }
+ payload.setProperties(props);
+ return payload.toJson();
+ }
+
+ public static String buildUpsertEdgeResponse(Edge edge, String version) throws CrudException {
+ return buildGetEdgeResponse(edge, version);
+ }
+
+ public static String buildGetVertexResponse(Vertex vertex, List<Edge> edges, String version)
+ throws CrudException {
+ VertexPayload vertexPayload = new VertexPayload();
+ vertexPayload.setId(vertex.getId().get());
+ vertexPayload.setType(vertex.getType());
+ vertexPayload.setUrl(URL_BASE + version + "/" + vertex.getType() + "/" + vertex.getId().get());
+ JsonObject props = new JsonObject();
+ for (String key : vertex.getProperties().keySet()) {
+ addJsonProperperty(props, key, vertex.getProperties().get(key));
+ }
+ vertexPayload.setProperties(props);
+ List<EdgePayload> inEdges = new ArrayList<EdgePayload>();
+ List<EdgePayload> outEdges = new ArrayList<EdgePayload>();
+ for (Edge e : edges) {
+ if (e.getTarget().getId().get().equals(vertex.getId().get())) {
+ EdgePayload inEdge = new EdgePayload();
+ inEdge.setId(e.getId().get());
+ inEdge.setType(e.getType());
+ inEdge.setUrl(URL_BASE + "relationships/"
+ + RelationshipSchemaLoader.getLatestSchemaVersion()
+ + "/" + e.getType() + "/" + e.getId().get());
+ inEdge.setSource(
+ URL_BASE + version + "/" + e.getSource().getType() + "/" + e.getSource().getId().get());
+
+ inEdges.add(inEdge);
+ } else if (e.getSource().getId().get().equals(vertex.getId().get())) {
+ EdgePayload outEdge = new EdgePayload();
+ outEdge.setId(e.getId().get());
+ outEdge.setType(e.getType());
+ outEdge.setUrl(URL_BASE + "relationships/"
+ + RelationshipSchemaLoader.getLatestSchemaVersion()
+ + "/" + e.getType() + "/" + e.getId().get());
+ outEdge.setTarget(
+ URL_BASE + version + "/" + e.getTarget().getType() + "/" + e.getTarget().getId().get());
+ outEdges.add(outEdge);
+ }
+ }
+
+
+ vertexPayload.setIn(inEdges);
+ vertexPayload.setOut(outEdges);
+
+ return vertexPayload.toJson();
+ }
+
+ public static String buildGetVerticesResponse(List<Vertex> items, String version)
+ throws CrudException {
+
+ JsonArray arry = new JsonArray();
+ for (Vertex v : items) {
+ JsonObject item = new JsonObject();
+ item.addProperty("id", v.getId().get());
+ item.addProperty("type", v.getType());
+ item.addProperty("url", "services/inventory/" + version + "/"
+ + v.getType() + "/" + v.getId().get());
+
+ arry.add(item);
+ }
+
+ return gson.toJson(arry);
+ }
+
+ public static String buildGetEdgeResponse(Edge edge, String version) throws CrudException {
+
+ EdgePayload payload = new EdgePayload();
+ payload.setId(edge.getId().get());
+ payload.setType(edge.getType());
+ payload.setUrl(URL_BASE + "relationships/" + version + "/" + edge.getType()
+ + "/" + edge.getId().get());
+ payload.setSource(
+ URL_BASE + version + "/" + edge.getSource().getType()
+ + "/" + edge.getSource().getId().get());
+ payload.setTarget(
+ URL_BASE + version + "/" + edge.getTarget().getType()
+ + "/" + edge.getTarget().getId().get());
+
+ JsonObject props = new JsonObject();
+ for (String key : edge.getProperties().keySet()) {
+ addJsonProperperty(props, key, edge.getProperties().get(key));
+ }
+ payload.setProperties(props);
+ return payload.toJson();
+ }
+
+ public static String buildGetEdgesResponse(List<Edge> items, String version)
+ throws CrudException {
+
+ JsonArray arry = new JsonArray();
+ for (Edge e : items) {
+ JsonObject item = new JsonObject();
+ item.addProperty("id", e.getId().get());
+ item.addProperty("type", e.getType());
+ item.addProperty("url", URL_BASE + "relationships/" + version + "/" + e.getType()
+ + "/" + e.getId().get());
+ item.addProperty(SOURCE, "services/inventory/" + version + "/" + e.getSource().getType()
+ + "/" + e.getSource().getId().get());
+ item.addProperty(TARGET, "services/inventory/" + version + "/" + e.getTarget().getType()
+ + "/" + e.getTarget().getId().get());
+ arry.add(item);
+ }
+
+ return gson.toJson(arry);
+ }
+
+ private static void addJsonProperperty(JsonObject jsonObj, String key, Object value) {
+ if (value instanceof Integer) {
+ jsonObj.addProperty(key, (Integer) value);
+ } else if (value instanceof Boolean) {
+ jsonObj.addProperty(key, (Boolean) value);
+ } else if (value instanceof Double) {
+ jsonObj.addProperty(key, (Double) value);
+ } else if (value instanceof String) {
+ jsonObj.addProperty(key, (String) value);
+ } else {
+ jsonObj.addProperty(key, value.toString());
+ }
+ }
+
+}
diff --git a/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java b/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java
new file mode 100644
index 0000000..ce60c3c
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java
@@ -0,0 +1,209 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.service;
+
+import com.att.ecomp.event.api.EventPublisher;
+
+import org.openecomp.crud.dao.GraphDao;
+import org.openecomp.crud.dao.champ.ChampDao;
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.parser.CrudResponseBuilder;
+import org.openecomp.crud.util.CrudProperties;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.openecomp.schema.OxmModelLoader;
+import org.openecomp.schema.OxmModelValidator;
+import org.openecomp.schema.RelationshipSchemaLoader;
+import org.openecomp.schema.RelationshipSchemaValidator;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+public class CrudGraphDataService {
+
+ private GraphDao dao;
+
+ public CrudGraphDataService(EventPublisher champEventPublisher) throws CrudException {
+
+ // Configure the GraphDao and wire it
+ Properties champProperties = new Properties();
+ champProperties.put(ChampDao.CONFIG_STORAGE_BACKEND, "titan");
+ champProperties.put(ChampDao.CONFIG_STORAGE_BACKEND_DB,
+ CrudProperties.get(CrudServiceConstants.CRD_STORAGE_BACKEND_DB, "hbase"));
+ champProperties.put(ChampDao.CONFIG_STORAGE_HOSTNAMES,
+ CrudProperties.get(CrudServiceConstants.CRD_GRAPH_HOST));
+ champProperties.put(ChampDao.CONFIG_STORAGE_PORT,
+ CrudProperties.get(CrudServiceConstants.CRD_GRAPH_PORT, "2181"));
+ champProperties.put(ChampDao.CONFIG_HBASE_ZNODE_PARENT,
+ CrudProperties.get(CrudServiceConstants.CRD_HBASE_ZNODE_PARENT, "/hbase-unsecure"));
+
+ if (CrudProperties.get("crud.graph.name") != null) {
+ champProperties.put(ChampDao.CONFIG_GRAPH_NAME, CrudProperties.get("crud.graph.name"));
+ }
+
+ if (champEventPublisher != null) {
+ champProperties.put(ChampDao.CONFIG_EVENT_STREAM_PUBLISHER, champEventPublisher);
+ }
+
+ if (CrudProperties.get(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS) != null) {
+ champProperties.put(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS,
+ Integer.parseInt(CrudProperties.get(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS)));
+ }
+
+ ChampDao champDao = new ChampDao(champProperties);
+
+ this.dao = champDao;
+
+ //load the schemas
+ OxmModelLoader.loadModels();
+ RelationshipSchemaLoader.loadModels();
+ }
+
+
+ public String addVertex(String version, String type, VertexPayload payload) throws CrudException {
+ Vertex vertex = OxmModelValidator.validateIncomingUpsertPayload(null, version, type,
+ payload.getProperties());
+ return addVertex(version, vertex);
+ }
+
+ private String addVertex(String version, Vertex vertex) throws CrudException {
+ Vertex addedVertex = dao.addVertex(vertex.getType(), vertex.getProperties());
+ return CrudResponseBuilder
+ .buildUpsertVertexResponse(OxmModelValidator.validateOutgoingPayload(version, addedVertex),
+ version);
+ }
+
+ public String addEdge(String version, String type, EdgePayload payload) throws CrudException {
+ Edge edge = RelationshipSchemaValidator.validateIncomingAddPayload(version, type, payload);
+ return addEdge(version, edge);
+ }
+
+ private String addEdge(String version, Edge edge) throws CrudException {
+ Edge addedEdge = dao.addEdge(edge.getType(), edge.getSource(), edge.getTarget(),
+ edge.getProperties());
+ return CrudResponseBuilder.buildUpsertEdgeResponse(
+ RelationshipSchemaValidator.validateOutgoingPayload(version, addedEdge), version);
+ }
+
+ public String getEdge(String version, String id, String type) throws CrudException {
+ RelationshipSchemaValidator.validateType(version, type);
+ Edge edge = dao.getEdge(id, type);
+
+ return CrudResponseBuilder.buildGetEdgeResponse(RelationshipSchemaValidator
+ .validateOutgoingPayload(version, edge),
+ version);
+ }
+
+ public String getEdges(String version, String type, Map<String, String> filter)
+ throws CrudException {
+ RelationshipSchemaValidator.validateType(version, type);
+ List<Edge> items = dao.getEdges(type, RelationshipSchemaValidator
+ .resolveCollectionfilter(version, type, filter));
+ return CrudResponseBuilder.buildGetEdgesResponse(items, version);
+ }
+
+
+ public String updateVertex(String version, String id, String type, VertexPayload payload)
+ throws CrudException {
+ Vertex vertex = OxmModelValidator.validateIncomingUpsertPayload(id, version, type,
+ payload.getProperties());
+ return updateVertex(version, vertex);
+
+ }
+
+ private String updateVertex(String version, Vertex vertex) throws CrudException {
+ Vertex updatedVertex = dao.updateVertex(vertex.getId().get(), vertex.getType(),
+ vertex.getProperties());
+ return CrudResponseBuilder
+ .buildUpsertVertexResponse(OxmModelValidator.validateOutgoingPayload(version,
+ updatedVertex), version);
+ }
+
+ public String patchVertex(String version, String id, String type, VertexPayload payload)
+ throws CrudException {
+ Vertex existingVertex = dao.getVertex(id, OxmModelValidator.resolveCollectionType(version,
+ type));
+ Vertex vertex = OxmModelValidator.validateIncomingPatchPayload(id, version, type,
+ payload.getProperties(), existingVertex);
+ return updateVertex(version, vertex);
+
+ }
+
+ public String deleteVertex(String version, String id, String type) throws CrudException {
+ type = OxmModelValidator.resolveCollectionType(version, type);
+ dao.deleteVertex(id, type);
+ return "";
+
+ }
+
+ public String deleteEdge(String version, String id, String type) throws CrudException {
+ RelationshipSchemaValidator.validateType(version, type);
+ dao.deleteEdge(id, type);
+ return "";
+
+ }
+
+ public String updateEdge(String version, String id, String type, EdgePayload payload)
+ throws CrudException {
+ Edge edge = dao.getEdge(id, type);
+ Edge validatedEdge = RelationshipSchemaValidator.validateIncomingUpdatePayload(edge,
+ version, payload);
+ return updateEdge(version, validatedEdge);
+
+ }
+
+ private String updateEdge(String version, Edge edge) throws CrudException {
+ Edge updatedEdge = dao.updateEdge(edge);
+ return CrudResponseBuilder.buildUpsertEdgeResponse(
+ RelationshipSchemaValidator.validateOutgoingPayload(version, updatedEdge), version);
+ }
+
+ public String patchEdge(String version, String id, String type, EdgePayload payload)
+ throws CrudException {
+ Edge edge = dao.getEdge(id, type);
+ Edge patchedEdge = RelationshipSchemaValidator.validateIncomingPatchPayload(edge,
+ version, payload);
+ return updateEdge(version, patchedEdge);
+
+ }
+
+ public String getVertex(String version, String id, String type) throws CrudException {
+ type = OxmModelValidator.resolveCollectionType(version, type);
+ Vertex vertex = dao.getVertex(id, type);
+ List<Edge> edges = dao.getVertexEdges(id);
+ return CrudResponseBuilder.buildGetVertexResponse(OxmModelValidator
+ .validateOutgoingPayload(version, vertex), edges, version);
+ }
+
+ public String getVertices(String version, String type, Map<String, String> filter)
+ throws CrudException {
+ type = OxmModelValidator.resolveCollectionType(version, type);
+ List<Vertex> items = dao.getVertices(type, OxmModelValidator.resolveCollectionfilter(version,
+ type, filter));
+ return CrudResponseBuilder.buildGetVerticesResponse(items, version);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/crud/service/CrudRestService.java b/src/main/java/org/openecomp/crud/service/CrudRestService.java
new file mode 100644
index 0000000..09193ad
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/service/CrudRestService.java
@@ -0,0 +1,686 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.service;
+
+import org.apache.cxf.jaxrs.ext.PATCH;
+import org.openecomp.auth.Auth;
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.logging.CrudServiceMsgs;
+import org.openecomp.crud.logging.LoggingUtil;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.slf4j.MDC;
+
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.security.auth.x500.X500Principal;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
+
+public class CrudRestService {
+
+ private CrudGraphDataService crudGraphDataService;
+ Logger logger = LoggerFactory.getInstance().getLogger(CrudRestService.class.getName());
+ Logger auditLogger = LoggerFactory.getInstance().getAuditLogger(CrudRestService.class.getName());
+ private Auth auth;
+
+ private String mediaType = MediaType.APPLICATION_JSON;
+ public static final String HTTP_PATCH_METHOD_OVERRIDE = "X-HTTP-Method-Override";
+
+ public CrudRestService(CrudGraphDataService crudGraphDataService) throws Exception {
+ this.crudGraphDataService = crudGraphDataService;
+ this.auth = new Auth(CrudServiceConstants.CRD_AUTH_FILE);
+ }
+
+ public enum Action {
+ POST, GET, PUT, DELETE, PATCH
+ }
+
+ ;
+
+ public void startup() {
+
+ }
+
+ @GET
+ @Path("/{version}/{type}/{id}")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response getVertex(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("id") String id,
+ @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+
+ if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+ try {
+ String result = crudGraphDataService.getVertex(version, id, type);
+ response = Response.status(Status.OK).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @GET
+ @Path("/{version}/{type}/")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response getVertices(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("uri") @Encoded String uri,
+ @Context HttpHeaders headers, @Context UriInfo uriInfo,
+ @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+ if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+ Map<String, String> filter = new HashMap<String, String>();
+ for (Map.Entry<String, List<String>> e : uriInfo.getQueryParameters().entrySet()) {
+ filter.put(e.getKey(), e.getValue().get(0));
+ }
+
+ try {
+ String result = crudGraphDataService.getVertices(version, type, filter);
+ response = Response.status(Status.OK).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @GET
+ @Path("/relationships/{version}/{type}/{id}")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response getEdge(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("id") String id,
+ @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+
+ if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+ try {
+
+ String result = crudGraphDataService.getEdge(version, id, type);
+ response = Response.status(Status.OK).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @GET
+ @Path("/relationships/{version}/{type}/")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response getEdges(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("uri") @Encoded String uri,
+ @Context HttpHeaders headers, @Context UriInfo uriInfo,
+ @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+
+ if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+ Map<String, String> filter = new HashMap<String, String>();
+ for (Map.Entry<String, List<String>> e : uriInfo.getQueryParameters().entrySet()) {
+ filter.put(e.getKey(), e.getValue().get(0));
+ }
+
+ try {
+ String result = crudGraphDataService.getEdges(version, type, filter);
+ response = Response.status(Status.OK).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @PUT
+ @Path("/relationships/{version}/{type}/{id}")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response updateEdge(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("id") String id,
+ @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+
+ if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+ try {
+ EdgePayload payload = EdgePayload.fromJson(content);
+ if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+ throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+ }
+ if (payload.getId() != null && !payload.getId().equals(id)) {
+ throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
+ }
+ String result;
+
+ if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null
+ && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE)
+ .equalsIgnoreCase("PATCH")) {
+ result = crudGraphDataService.patchEdge(version, id, type, payload);
+ } else {
+
+ result = crudGraphDataService.updateEdge(version, id, type, payload);
+ }
+
+ response = Response.status(Status.OK).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @PATCH
+ @Path("/relationships/{version}/{type}/{id}")
+ @Consumes({"application/merge-patch+json"})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response patchEdge(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("id") String id,
+ @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+ if (validateRequest(req, uri, content, Action.PATCH,
+ CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+ try {
+ EdgePayload payload = EdgePayload.fromJson(content);
+ if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+ throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+ }
+ if (payload.getId() != null && !payload.getId().equals(id)) {
+ throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
+ }
+
+ String result = crudGraphDataService.patchEdge(version, id, type, payload);
+ response = Response.status(Status.OK).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @PUT
+ @Path("/{version}/{type}/{id}")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response updateVertex(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("id") String id,
+ @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+
+ if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+ try {
+ VertexPayload payload = VertexPayload.fromJson(content);
+ if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+ throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+ }
+ if (payload.getId() != null && !payload.getId().equals(id)) {
+ throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
+ }
+ String result;
+ if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null
+ && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE)
+ .equalsIgnoreCase("PATCH")) {
+ result = crudGraphDataService.patchVertex(version, id, type, payload);
+ } else {
+
+ result = crudGraphDataService.updateVertex(version, id, type, payload);
+ }
+ response = Response.status(Status.OK).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @PATCH
+ @Path("/{version}/{type}/{id}")
+ @Consumes({"application/merge-patch+json"})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response patchVertex(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("id") String id,
+ @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+
+ if (validateRequest(req, uri, content, Action.PATCH,
+ CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+ try {
+ VertexPayload payload = VertexPayload.fromJson(content);
+ if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+ throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+ }
+ if (payload.getId() != null && !payload.getId().equals(id)) {
+ throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
+ }
+
+ String result = crudGraphDataService.patchVertex(version, id, type, payload);
+ response = Response.status(Status.OK).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @POST
+ @Path("/{version}/{type}/")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response addVertex(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("uri") @Encoded String uri,
+ @Context HttpHeaders headers, @Context UriInfo uriInfo,
+ @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+
+ if (validateRequest(req, uri, content, Action.POST,
+ CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+ try {
+ VertexPayload payload = VertexPayload.fromJson(content);
+ if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+ throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+ }
+ if (payload.getId() != null) {
+ throw new CrudException("ID specified , use Http PUT to update Vertex",
+ Status.BAD_REQUEST);
+ }
+
+ if (payload.getType() != null && !payload.getType().equals(type)) {
+ throw new CrudException("Vertex Type mismatch", Status.BAD_REQUEST);
+ }
+
+ String result = crudGraphDataService.addVertex(version, type, payload);
+ response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @POST
+ @Path("/{version}/")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response addVertex(String content, @PathParam("version") String version,
+ @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+
+ if (validateRequest(req, uri, content, Action.POST,
+ CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+ try {
+
+ VertexPayload payload = VertexPayload.fromJson(content);
+ if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+ throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+ }
+ if (payload.getId() != null) {
+ throw new CrudException("ID specified , use Http PUT to update Vertex",
+ Status.BAD_REQUEST);
+ }
+
+ if (payload.getType() == null || payload.getType().isEmpty()) {
+ throw new CrudException("Missing Vertex Type ", Status.BAD_REQUEST);
+ }
+ String result = crudGraphDataService.addVertex(version, payload.getType(), payload);
+ response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @POST
+ @Path("/relationships/{version}/{type}/")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response addEdge(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("uri") @Encoded String uri,
+ @Context HttpHeaders headers, @Context UriInfo uriInfo,
+ @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+
+ if (validateRequest(req, uri, content, Action.POST,
+ CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+
+ try {
+ EdgePayload payload = EdgePayload.fromJson(content);
+ if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+ throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+ }
+ if (payload.getId() != null) {
+ throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST);
+ }
+
+ if (payload.getType() != null && !payload.getType().equals(type)) {
+ throw new CrudException("Edge Type mismatch", Status.BAD_REQUEST);
+ }
+ String result = crudGraphDataService.addEdge(version, type, payload);
+ response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @POST
+ @Path("/relationships/{version}/")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response addEdge(String content, @PathParam("version") String version,
+ @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+
+ if (validateRequest(req, uri, content, Action.POST,
+ CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+
+ try {
+ EdgePayload payload = EdgePayload.fromJson(content);
+ if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
+ throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
+ }
+ if (payload.getId() != null) {
+ throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST);
+ }
+
+ if (payload.getType() == null || payload.getType().isEmpty()) {
+ throw new CrudException("Missing Edge Type ", Status.BAD_REQUEST);
+ }
+ String result = crudGraphDataService.addEdge(version, payload.getType(), payload);
+
+ response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @DELETE
+ @Path("/{version}/{type}/{id}")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response deleteVertex(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("id") String id,
+ @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+
+ if (validateRequest(req, uri, content, Action.DELETE,
+ CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+
+ try {
+ String result = crudGraphDataService.deleteVertex(version, id, type);
+ response = Response.status(Status.OK).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ @DELETE
+ @Path("/relationships/{version}/{type}/{id}")
+ @Consumes({MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_JSON})
+ public Response deleteEdge(String content, @PathParam("version") String version,
+ @PathParam("type") String type, @PathParam("id") String id,
+ @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo, @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+
+ logger.debug("Incoming request..." + content);
+ Response response = null;
+ if (validateRequest(req, uri, content, Action.DELETE,
+ CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {
+
+ try {
+ String result = crudGraphDataService.deleteEdge(version, id, type);
+ response = Response.status(Status.OK).entity(result).type(mediaType).build();
+ } catch (CrudException ce) {
+ response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
+ } catch (Exception e) {
+ response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+ }
+ } else {
+ response = Response.status(Status.FORBIDDEN).entity(content)
+ .type(MediaType.APPLICATION_JSON).build();
+ }
+
+ LoggingUtil.logRestRequest(logger, auditLogger, req, response);
+ return response;
+ }
+
+ protected boolean validateRequest(HttpServletRequest req, String uri, String content,
+ Action action, String authPolicyFunctionName) {
+ try {
+ String cipherSuite = (String) req.getAttribute("javax.servlet.request.cipher_suite");
+ String authUser = null;
+ if (cipherSuite != null) {
+ X509Certificate[] certChain = (X509Certificate[]) req
+ .getAttribute("javax.servlet.request.X509Certificate");
+ X509Certificate clientCert = certChain[0];
+ X500Principal subjectDn = clientCert.getSubjectX500Principal();
+ authUser = subjectDn.toString();
+ }
+ return this.auth.validateRequest(authUser.toLowerCase(), action.toString()
+ + ":" + authPolicyFunctionName);
+ } catch (Exception e) {
+ logResult(action, uri, e);
+ return false;
+ }
+ }
+
+ void logResult(Action op, String uri, Exception e) {
+
+ logger.error(CrudServiceMsgs.EXCEPTION_DURING_METHOD_CALL, op.toString(), uri,
+ e.getStackTrace().toString());
+
+ // Clear the MDC context so that no other transaction inadvertently
+ // uses our transaction id.
+ MDC.clear();
+ }
+}
diff --git a/src/main/java/org/openecomp/crud/service/EdgePayload.java b/src/main/java/org/openecomp/crud/service/EdgePayload.java
new file mode 100644
index 0000000..d098d16
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/service/EdgePayload.java
@@ -0,0 +1,115 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.service;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+
+import org.openecomp.crud.exception.CrudException;
+
+import javax.ws.rs.core.Response.Status;
+
+public class EdgePayload {
+
+ private String id;
+ private String type;
+ private String url;
+ private String source;
+ private String target;
+ private JsonElement properties;
+
+ private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+
+
+ @Override
+ public String toString() {
+ return "EdgePayload [id=" + id + ", type=" + type + ", url=" + url + ", source="
+ + source + ", target=" + target + ", properties=" + properties + "]";
+ }
+
+ public String toJson() {
+ return gson.toJson(this);
+ }
+
+ public static EdgePayload fromJson(String payload) throws CrudException {
+ try {
+ if (payload == null || payload.isEmpty()) {
+ throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST);
+ }
+ return gson.fromJson(payload, EdgePayload.class);
+ } catch (Exception ex) {
+ throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST);
+ }
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+ public String getTarget() {
+ return target;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
+
+ public JsonElement getProperties() {
+ return properties;
+ }
+
+ public void setProperties(JsonElement properties) {
+ this.properties = properties;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/openecomp/crud/service/JaxrsEchoService.java b/src/main/java/org/openecomp/crud/service/JaxrsEchoService.java
new file mode 100644
index 0000000..0edd316
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/service/JaxrsEchoService.java
@@ -0,0 +1,63 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.service;
+
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.logging.LoggingUtil;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
+
+
+public class JaxrsEchoService {
+
+ private static Logger logger = LoggerFactory.getInstance()
+ .getLogger(JaxrsEchoService.class.getName());
+ private static Logger auditLogger = LoggerFactory.getInstance()
+ .getAuditLogger(JaxrsEchoService.class.getName());
+
+ @GET
+ @Path("echo/{input}")
+ @Produces("text/plain")
+ public String ping(@PathParam("input") String input,
+ @Context HttpHeaders headers,
+ @Context UriInfo info,
+ @Context HttpServletRequest req) {
+
+ LoggingUtil.initMdcContext(req, headers);
+ LoggingUtil.logRestRequest(logger, auditLogger, req, Response.status(Status.OK)
+ .entity("OK").build());
+
+ return "Hello, " + input + ".";
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/openecomp/crud/service/VertexPayload.java b/src/main/java/org/openecomp/crud/service/VertexPayload.java
new file mode 100644
index 0000000..ed79002
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/service/VertexPayload.java
@@ -0,0 +1,117 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.service;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.ws.rs.core.Response.Status;
+
+public class VertexPayload {
+
+ private String id;
+ private String type;
+ private String url;
+ private JsonElement properties;
+ private List<EdgePayload> in = new ArrayList<EdgePayload>();
+ private List<EdgePayload> out = new ArrayList<EdgePayload>();
+
+ private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+
+ public String toJson() {
+ return gson.toJson(this);
+ }
+
+ public static VertexPayload fromJson(String payload) throws CrudException {
+ try {
+ if (payload == null || payload.isEmpty()) {
+ throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST);
+ }
+ return gson.fromJson(payload, VertexPayload.class);
+ } catch (Exception ex) {
+ throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST);
+ }
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public JsonElement getProperties() {
+ return properties;
+ }
+
+ public void setProperties(JsonElement properties) {
+ this.properties = properties;
+ }
+
+ public List<EdgePayload> getIn() {
+ return in;
+ }
+
+ public void setIn(List<EdgePayload> in) {
+ this.in = in;
+ }
+
+ public List<EdgePayload> getOut() {
+ return out;
+ }
+
+ public void setOut(List<EdgePayload> out) {
+ this.out = out;
+ }
+
+
+ @Override
+ public String toString() {
+ return "VertexPayload [id=" + id + ", type=" + type + ", url=" + url + ", properties="
+ + properties + ", in=" + in + ", out=" + out + "]";
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java b/src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java
new file mode 100644
index 0000000..c1a1e18
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java
@@ -0,0 +1,92 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.util;
+
+import org.eclipse.persistence.dynamic.DynamicEntity;
+import org.eclipse.persistence.jaxb.JAXBMarshaller;
+import org.eclipse.persistence.jaxb.MarshallerProperties;
+import org.eclipse.persistence.jaxb.UnmarshallerProperties;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import javax.ws.rs.core.MediaType;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+
+public class CrudJaxbTransformation {
+ /**
+ * Marshal a dynamic entity into a string.
+ *
+ * @param entity the dynamic entity
+ * @param jaxbContext the dynamic jaxb context
+ * @return the marshaled entity
+ * @throws RouterException on error
+ */
+ public static String marshal(MediaType mediaType, final DynamicEntity entity,
+ final DynamicJAXBContext jaxbContext) throws JAXBException {
+
+ final JAXBMarshaller marshaller = jaxbContext.createMarshaller();
+ marshaller.setProperty(JAXBMarshaller.JAXB_FORMATTED_OUTPUT, false);
+
+ if (MediaType.APPLICATION_JSON_TYPE.isCompatible(mediaType)) {
+ marshaller.setProperty("eclipselink.media-type", "application/json");
+ marshaller.setProperty("eclipselink.json.include-root", false);
+ marshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, Boolean.FALSE);
+ }
+
+ final StringWriter writer = new StringWriter();
+ marshaller.marshal(entity, writer);
+ return writer.toString();
+
+ }
+
+ /**
+ * @param type
+ * @param json
+ * @param mediaType
+ * @return
+ * @throws JAXBException
+ * @throws Exception
+ */
+ public static Object unmarshal(String javaClass, String content, MediaType mediaType,
+ final DynamicJAXBContext jaxbContext) throws JAXBException {
+ Object clazz = null;
+ Unmarshaller unmarshaller = null;
+
+ clazz = jaxbContext.newDynamicEntity(javaClass);
+
+ unmarshaller = jaxbContext.createUnmarshaller();
+ if (mediaType.equals(MediaType.APPLICATION_JSON_TYPE)) {
+ unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
+ unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
+ unmarshaller.setProperty(UnmarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);
+ }
+
+ return unmarshaller.unmarshal(new StreamSource(new StringReader(content)),
+ clazz.getClass()).getValue();
+ }
+
+}
diff --git a/src/main/java/org/openecomp/crud/util/CrudProperties.java b/src/main/java/org/openecomp/crud/util/CrudProperties.java
new file mode 100644
index 0000000..69b2e16
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/util/CrudProperties.java
@@ -0,0 +1,77 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+public class CrudProperties {
+
+ private static Properties properties;
+
+ static {
+ properties = new Properties();
+ File file = new File(CrudServiceConstants.CRD_CONFIG_FILE);
+ try {
+ properties.load(new FileInputStream(file));
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static String get(String key) {
+ return properties.getProperty(key);
+ }
+
+ public static String get(String key, String defaultValue) {
+ return properties.getProperty(key, defaultValue);
+ }
+
+ public static void put(String key, String value) {
+ properties.setProperty(key, value);
+ FileOutputStream fileOut = null;
+ try {
+ fileOut = new FileOutputStream(new File(CrudServiceConstants.CRD_CONFIG_FILE));
+ properties.store(fileOut, "Added property: " + key);
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+
+ try {
+ fileOut.close();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ }
+
+
+}
diff --git a/src/main/java/org/openecomp/crud/util/CrudServiceConstants.java b/src/main/java/org/openecomp/crud/util/CrudServiceConstants.java
new file mode 100644
index 0000000..9543e2d
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/util/CrudServiceConstants.java
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.util;
+
+public class CrudServiceConstants {
+ public static final String CRD_SERVICE_NAME = "Crud-Service";
+
+ public static final String CRD_FILESEP = (System.getProperty("file.separator") == null) ? "/"
+ : System.getProperty("file.separator");
+
+ public static final String CRD_SPECIFIC_CONFIG = System.getProperty("CONFIG_HOME") + CRD_FILESEP;
+
+ public static final String CRD_HOME_MODEL = CRD_SPECIFIC_CONFIG + "model" + CRD_FILESEP;
+ public static final String CRD_HOME_AUTH = CRD_SPECIFIC_CONFIG + "auth" + CRD_FILESEP;
+
+ public static final String CRD_GRAPH_HOST = "crud.graph.host";
+ public static final String CRD_GRAPH_PORT = "crud.graph.port";
+ public static final String CRD_GRAPH_NAME = "crud.graph.name";
+ public static final String CRD_STORAGE_BACKEND_DB = "crud.storage.backend.db";
+ public static final String CRD_HBASE_ZNODE_PARENT
+ = "crud.storage.hbase.ext.zookeeper.znode.parent";
+
+ public static final String CRD_CONFIG_FILE = CRD_SPECIFIC_CONFIG + "crud-api.properties";
+ public static final String CRD_AUTH_FILE = CRD_HOME_AUTH + "crud_policy.json";
+
+ public static final String CRD_AUTH_POLICY_NAME = "crud";
+
+ public static final String CRD_EVENT_STREAM_HOSTS = "event.stream.hosts";
+
+
+
+}
diff --git a/src/main/java/org/openecomp/crud/util/CrudServiceUtil.java b/src/main/java/org/openecomp/crud/util/CrudServiceUtil.java
new file mode 100644
index 0000000..25e3de6
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/util/CrudServiceUtil.java
@@ -0,0 +1,56 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.util;
+
+import org.openecomp.crud.exception.CrudException;
+
+import javax.ws.rs.core.Response.Status;
+
+public class CrudServiceUtil {
+
+
+ public static Object validateFieldType(String value, Class clazz) throws CrudException {
+ try {
+ if (clazz.isAssignableFrom(Integer.class)) {
+ return Integer.parseInt(value);
+ } else if (clazz.isAssignableFrom(Long.class)) {
+ return Long.parseLong(value);
+ } else if (clazz.isAssignableFrom(Float.class)) {
+ return Float.parseFloat(value);
+ } else if (clazz.isAssignableFrom(Double.class)) {
+ return Double.parseDouble(value);
+ } else if (clazz.isAssignableFrom(Boolean.class)) {
+ if (!value.equals("true") && !value.equals("false")) {
+ throw new CrudException("Invalid propertry value: " + value, Status.BAD_REQUEST);
+ }
+ return Boolean.parseBoolean(value);
+ } else {
+ return value;
+ }
+ } catch (Exception e) {
+ throw new CrudException("Invalid property value: " + value, Status.BAD_REQUEST);
+ }
+ }
+
+}
diff --git a/src/main/java/org/openecomp/crud/util/FileWatcher.java b/src/main/java/org/openecomp/crud/util/FileWatcher.java
new file mode 100644
index 0000000..8c7cbc1
--- /dev/null
+++ b/src/main/java/org/openecomp/crud/util/FileWatcher.java
@@ -0,0 +1,48 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.crud.util;
+
+import java.io.File;
+import java.util.TimerTask;
+
+public abstract class FileWatcher extends TimerTask {
+ private long timeStamp;
+ private File file;
+
+ public FileWatcher(File file) {
+ this.file = file;
+ this.timeStamp = file.lastModified();
+ }
+
+ public final void run() {
+ long timeStamp = file.lastModified();
+
+ if ((timeStamp - this.timeStamp) > 500) {
+ this.timeStamp = timeStamp;
+ onChange(file);
+ }
+ }
+
+ protected abstract void onChange(File file);
+} \ No newline at end of file
diff --git a/src/main/java/org/openecomp/schema/OxmModelLoader.java b/src/main/java/org/openecomp/schema/OxmModelLoader.java
new file mode 100644
index 0000000..4ef77a2
--- /dev/null
+++ b/src/main/java/org/openecomp/schema/OxmModelLoader.java
@@ -0,0 +1,168 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.schema;
+
+import org.eclipse.persistence.jaxb.JAXBContextProperties;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.logging.CrudServiceMsgs;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.openecomp.crud.util.FileWatcher;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.ws.rs.core.Response.Status;
+import javax.xml.bind.JAXBException;
+
+
+public class OxmModelLoader {
+
+ private static Map<String, DynamicJAXBContext> versionContextMap
+ = new ConcurrentHashMap<String, DynamicJAXBContext>();
+ private static Map<String, Timer> timers = new ConcurrentHashMap<String, Timer>();
+
+ final static Pattern p = Pattern.compile("aai_oxm_(.*).xml");
+
+ private static org.openecomp.cl.api.Logger logger = LoggerFactory.getInstance()
+ .getLogger(OxmModelLoader.class.getName());
+
+ public synchronized static void loadModels() throws CrudException {
+ ClassLoader cl = OxmModelLoader.class.getClassLoader();
+ ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
+ Resource[] resources;
+ try {
+ resources = resolver.getResources("classpath*:/oxm/aai_oxm*.xml");
+ } catch (IOException ex) {
+ logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, ex.getMessage());
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+
+ if (resources.length == 0) {
+ logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, "No OXM schema files found on classpath");
+ throw new CrudException("Failed to load schema", Status.NOT_FOUND);
+ }
+
+ for (Resource resource : resources) {
+ Matcher matcher = p.matcher(resource.getFilename());
+
+ if (matcher.matches()) {
+ try {
+ OxmModelLoader.loadModel(matcher.group(1), resource);
+ } catch (Exception e) {
+ logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, "Failed to load " + resource.getFilename()
+ + ": " + e.getMessage());
+ throw new CrudException("Failed to load schema", Status.NOT_FOUND);
+ }
+ }
+ }
+ }
+
+ private static void addtimer(String version, File file) {
+ TimerTask task = null;
+ task = new FileWatcher(
+ file) {
+ protected void onChange(File file) {
+ // here we implement the onChange
+ logger.info(CrudServiceMsgs.OXM_FILE_CHANGED, file.getName());
+
+ try {
+ OxmModelLoader.loadModel(version, file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ };
+
+ if (!timers.containsKey(version)) {
+ Timer timer = new Timer("oxm-" + version);
+ timer.schedule(task, new Date(), 10000);
+ timers.put(version, timer);
+
+ }
+ }
+
+ private synchronized static void loadModel(String version, File file)
+ throws JAXBException, IOException {
+ InputStream inputStream = new FileInputStream(file);
+ loadModel(version, file.getName(), inputStream);
+ addtimer(version, file);
+ }
+
+ private synchronized static void loadModel(String version, Resource resource)
+ throws JAXBException, IOException {
+ InputStream inputStream = resource.getInputStream();
+ loadModel(version, resource.getFilename(), inputStream);
+ }
+
+ private synchronized static void loadModel(String version, String resourceName,
+ InputStream inputStream)
+ throws JAXBException, IOException {
+ Map<String, Object> properties = new HashMap<String, Object>();
+ properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, inputStream);
+ final DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory
+ .createContextFromOXM(Thread.currentThread().getContextClassLoader(), properties);
+ versionContextMap.put(version, jaxbContext);
+ logger.info(CrudServiceMsgs.LOADED_OXM_FILE, resourceName);
+ }
+
+ public static DynamicJAXBContext getContextForVersion(String version) throws CrudException {
+ if (versionContextMap == null || versionContextMap.isEmpty()) {
+ loadModels();
+ } else if (!versionContextMap.containsKey(version)) {
+ try {
+ loadModel(version, new File(CrudServiceConstants.CRD_HOME_MODEL + "aai_oxm_"
+ + version + ".xml"));
+ } catch (Exception e) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+ }
+
+ return versionContextMap.get(version);
+ }
+
+ public static Map<String, DynamicJAXBContext> getVersionContextMap() {
+ return versionContextMap;
+ }
+
+ public static void setVersionContextMap(Map<String, DynamicJAXBContext> versionContextMap) {
+ OxmModelLoader.versionContextMap = versionContextMap;
+ }
+
+}
diff --git a/src/main/java/org/openecomp/schema/OxmModelValidator.java b/src/main/java/org/openecomp/schema/OxmModelValidator.java
new file mode 100644
index 0000000..45a2597
--- /dev/null
+++ b/src/main/java/org/openecomp/schema/OxmModelValidator.java
@@ -0,0 +1,325 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.schema;
+
+import com.google.common.base.CaseFormat;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+
+import org.eclipse.persistence.dynamic.DynamicType;
+import org.eclipse.persistence.internal.helper.DatabaseField;
+import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
+import org.eclipse.persistence.mappings.DatabaseMapping;
+import org.eclipse.persistence.oxm.XMLField;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.util.CrudServiceUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.core.Response.Status;
+
+public class OxmModelValidator {
+ public enum Metadata {
+ NODE_TYPE("aai-node-type"),
+ URI("aai-uri"),
+ CREATED_TS("aai-created-ts"),
+ SOT("source-of-truth"),
+ LAST_MOD_SOT("last-mod-source-of-truth");
+
+ private final String propName;
+
+ Metadata(String propName) {
+ this.propName = propName;
+ }
+
+ public String propertyName() {
+ return propName;
+ }
+
+ public static boolean isProperty(String property) {
+ for (Metadata meta : Metadata.values()) {
+ if (meta.propName.equals(property)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+
+ public static Map<String, Object> resolveCollectionfilter(String version, String type,
+ Map<String, String> filter)
+ throws CrudException {
+
+ DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+
+ Map<String, Object> result = new HashMap<String, Object>();
+ if (jaxbContext == null) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+ final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL
+ .to(CaseFormat.UPPER_CAMEL,
+ CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)));
+
+ for (String key : filter.keySet()) {
+ String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, key);
+ if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) != null) {
+ try {
+ DatabaseMapping mapping = modelObjectType.getDescriptor()
+ .getMappingForAttributeName(keyJavaName);
+ Object value = CrudServiceUtil.validateFieldType(filter.get(key),
+ mapping.getField().getType());
+ result.put(key, value);
+ } catch (Exception ex) {
+ // Skip any exceptions thrown while validating the filter
+ // key value
+ continue;
+ }
+ }
+ }
+
+ return result;
+
+ }
+
+ public static String resolveCollectionType(String version, String type) throws CrudException {
+
+ DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+
+ if (jaxbContext == null) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+ // Determine if the Object part is a collection type in the model
+ // definition
+ final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL
+ .to(CaseFormat.UPPER_CAMEL,
+ CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)));
+
+ if (modelObjectType == null) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+
+ if (modelObjectType.getDescriptor().getMappings().size() == 1
+ && modelObjectType.getDescriptor().getMappings().get(0).isCollectionMapping()) {
+ String childJavaObjectName = modelObjectType.getDescriptor().getMappings()
+ .get(0).getAttributeName();
+ childJavaObjectName = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, childJavaObjectName);
+ final DynamicType childObjectType = jaxbContext.getDynamicType(childJavaObjectName);
+ if (childObjectType == null) {
+ // Should not happen as child object is defined in oxm model
+ // itself
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+ return childObjectType.getDescriptor().getTableName();
+ } else {
+ return modelObjectType.getDescriptor().getTableName();
+ }
+
+ }
+
+
+ public static Vertex validateIncomingUpsertPayload(String id, String version, String type,
+ JsonElement properties)
+ throws CrudException {
+
+ try {
+ type = resolveCollectionType(version, type);
+ DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+ String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
+ CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type));
+
+ final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
+
+ Set<Map.Entry<String, JsonElement>> payloadEntriesSet = properties.getAsJsonObject()
+ .entrySet();
+
+ //loop through input to validate against schema
+ for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
+ String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey());
+
+ // check for valid field
+ if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) {
+ throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST);
+ }
+
+ }
+
+ Map<String, JsonElement> entriesMap = new HashMap<String, JsonElement>();
+ for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
+ entriesMap.put(entry.getKey(), entry.getValue());
+ }
+
+ Vertex.Builder modelVertexBuilder = new Vertex.Builder(type);
+ if (id != null) {
+ modelVertexBuilder.id(id);
+ }
+ for (DatabaseMapping mapping : modelObjectType.getDescriptor().getMappings()) {
+ if (mapping.isAbstractDirectMapping()) {
+ DatabaseField field = mapping.getField();
+ String defaultValue = mapping.getProperties().get("defaultValue") == null ? ""
+ : mapping.getProperties().get("defaultValue").toString();
+
+ String keyName = field.getName().substring(0, field.getName().indexOf("/"));
+
+ if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName)
+ && !defaultValue.isEmpty()) {
+ modelVertexBuilder.property(keyName,
+ CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
+ }
+ // if schema field is required and not set then reject
+ if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName)
+ && defaultValue.isEmpty()) {
+ throw new CrudException("Missing required field: " + keyName, Status.BAD_REQUEST);
+ }
+ // If invalid field then reject
+ if (entriesMap.containsKey(keyName)) {
+ Object value = CrudServiceUtil.validateFieldType(entriesMap.get(keyName)
+ .getAsString(), field.getType());
+ modelVertexBuilder.property(keyName, value);
+ }
+
+ // Set defaults
+ if (!defaultValue.isEmpty() && !entriesMap.containsKey(keyName)) {
+ modelVertexBuilder.property(keyName,
+ CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
+ }
+ }
+ }
+
+ return modelVertexBuilder.build();
+ } catch (Exception e) {
+ throw new CrudException(e.getMessage(), Status.BAD_REQUEST);
+ }
+ }
+
+ public static Vertex validateIncomingPatchPayload(String id, String version, String type,
+ JsonElement properties, Vertex existingVertex)
+ throws CrudException {
+
+ try {
+ type = resolveCollectionType(version, type);
+ DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+ String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
+ CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type));
+
+ final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
+
+ Set<Map.Entry<String, JsonElement>> payloadEntriesSet = properties.getAsJsonObject()
+ .entrySet();
+
+ // Loop through the payload properties and merge with existing
+ // vertex props
+ for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
+
+ String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey());
+
+ // check for valid field
+ if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) {
+ throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST);
+ }
+
+ DatabaseField field = modelObjectType.getDescriptor()
+ .getMappingForAttributeName(keyJavaName).getField();
+ String defaultValue = modelObjectType.getDescriptor()
+ .getMappingForAttributeName(keyJavaName)
+ .getProperties().get("defaultValue") == null ? ""
+ : modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName)
+ .getProperties().get("defaultValue").toString();
+
+ // check if mandatory field is not set to null
+ if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
+ && !defaultValue.isEmpty()) {
+ existingVertex.getProperties().put(entry.getKey(),
+ CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
+ } else if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
+ && defaultValue.isEmpty()) {
+ throw new CrudException("Mandatory field: " + entry.getKey()
+ + " can't be set to null",
+ Status.BAD_REQUEST);
+ } else if (!((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
+ && existingVertex.getProperties().containsKey(entry.getKey())) {
+ existingVertex.getProperties().remove(entry.getKey());
+ } else if (!(entry.getValue() instanceof JsonNull)) {
+ // add/update the value if found in existing vertex
+ Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(),
+ field.getType());
+ existingVertex.getProperties().put(entry.getKey(), value);
+ }
+
+ }
+
+ return existingVertex;
+ } catch (Exception e) {
+ throw new CrudException(e.getMessage(), Status.BAD_REQUEST);
+ }
+
+ }
+
+ private static DatabaseField getDatabaseField(String fieldName, DynamicType modelObjectType) {
+ for (DatabaseField field : modelObjectType.getDescriptor().getAllFields()) {
+ int ix = field.getName().indexOf("/");
+ if (ix <= 0) {
+ ix = field.getName().length();
+ }
+
+ String keyName = field.getName().substring(0, ix);
+ if (fieldName.equals(keyName)) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+ public static Vertex validateOutgoingPayload(String version, Vertex vertex) {
+
+ Vertex.Builder modelVertexBuilder = new Vertex.Builder(vertex.getType())
+ .id(vertex.getId().get());
+
+ try {
+ DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
+ String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
+ CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL,
+ vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()) != null
+ ? vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()).toString() : vertex.getType()));
+ final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
+
+ for (String key : vertex.getProperties().keySet()) {
+ DatabaseField field = getDatabaseField(key, modelObjectType);
+ if (field != null) {
+ if (!Metadata.isProperty(key)) {
+ modelVertexBuilder.property(key, vertex.getProperties().get(key));
+ }
+ }
+ }
+ return modelVertexBuilder.build();
+ } catch (Exception ex) {
+ return vertex;
+ }
+
+ }
+
+
+}
diff --git a/src/main/java/org/openecomp/schema/RelationshipSchema.java b/src/main/java/org/openecomp/schema/RelationshipSchema.java
new file mode 100644
index 0000000..5b28e28
--- /dev/null
+++ b/src/main/java/org/openecomp/schema/RelationshipSchema.java
@@ -0,0 +1,138 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.schema;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.core.Response.Status;
+
+public class RelationshipSchema {
+ private static final Gson gson = new GsonBuilder().create();
+
+ public static final String SCHEMA_SOURCE_NODE_TYPE = "source-node-type";
+ public static final String SCHEMA_TARGET_NODE_TYPE = "target-node-type";
+ public static final String SCHEMA_RELATIONSHIP_TYPE = "relationship-type";
+ public static final String SCHEMA_RELATIONSHIP_TYPES_ARRAY = "relationship-types";
+ public static final String SCHEMA_RELATIONSHIP_PROPERTIES = "properties";
+ public static final String SCHEMA_RELATIONS_ARRAY = "relations";
+
+ /**
+ * key = source-node-type:target-node-type:relationship-type value = map of properties with name
+ * and type . Like propertyName:PropertyType
+ */
+ private HashMap<String, HashMap<String, Class<?>>> relations
+ = new HashMap<String, HashMap<String, Class<?>>>();
+ /**
+ * Hashmap of valid relationship types alongwith properrties.
+ */
+ private HashMap<String, HashMap<String, Class<?>>> relationTypes
+ = new HashMap<String, HashMap<String, Class<?>>>();
+
+
+ public RelationshipSchema(String json) throws CrudException {
+
+ JsonParser parser = new JsonParser();
+ try {
+ JsonObject root = parser.parse(json).getAsJsonObject();
+ JsonArray relationshipTypesArray = root.getAsJsonArray(SCHEMA_RELATIONSHIP_TYPES_ARRAY);
+ JsonArray relationsArray = root.getAsJsonArray(SCHEMA_RELATIONS_ARRAY);
+
+ //First load all the relationship-types
+ for (JsonElement item : relationshipTypesArray) {
+ JsonObject obj = item.getAsJsonObject();
+ String type = obj.get(SCHEMA_RELATIONSHIP_TYPE).getAsString();
+
+
+ HashMap<String, Class<?>> props = new HashMap<String, Class<?>>();
+ Set<Map.Entry<String, JsonElement>> entries = obj.get(SCHEMA_RELATIONSHIP_PROPERTIES)
+ .getAsJsonObject().entrySet();
+
+ for (Map.Entry<String, JsonElement> entry : entries) {
+ props.put(entry.getKey(), resolveClass(entry.getValue().getAsString()));
+
+ }
+ relationTypes.put(type, props);
+
+ }
+
+ for (JsonElement item : relationsArray) {
+ JsonObject obj = item.getAsJsonObject();
+ // Parse the Source/Taget nodeTypes
+
+ String relationType = obj.get(SCHEMA_RELATIONSHIP_TYPE).getAsString();
+ String key = obj.get(SCHEMA_SOURCE_NODE_TYPE).getAsString() + ":"
+ + obj.get(SCHEMA_TARGET_NODE_TYPE).getAsString() + ":" + relationType;
+
+
+ if (!relationTypes.containsKey(relationType)) {
+ throw new CrudException(SCHEMA_RELATIONSHIP_TYPE + ": " + relationType + " not found",
+ Status.BAD_REQUEST);
+ }
+
+ relations.put(key, relationTypes.get(relationType));
+ }
+ } catch (Exception e) {
+ throw new CrudException(e.getMessage(), Status.BAD_REQUEST);
+ }
+
+ }
+
+
+ public HashMap<String, Class<?>> lookupRelation(String key) {
+ return this.relations.get(key);
+ }
+
+ public HashMap<String, Class<?>> lookupRelationType(String type) {
+ return this.relationTypes.get(type);
+ }
+
+ public boolean isValidType(String type) {
+ return relationTypes.containsKey(type);
+ }
+
+ private Class<?> resolveClass(String type) throws CrudException, ClassNotFoundException {
+ Class<?> clazz = Class.forName(type);
+ validateClassTypes(clazz);
+ return clazz;
+ }
+
+ private void validateClassTypes(Class<?> clazz) throws CrudException {
+ if (!clazz.isAssignableFrom(Integer.class) && !clazz.isAssignableFrom(Double.class)
+ && !clazz.isAssignableFrom(Boolean.class) && !clazz.isAssignableFrom(String.class)) {
+ throw new CrudException("", Status.BAD_REQUEST);
+ }
+ }
+
+
+}
diff --git a/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java b/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java
new file mode 100644
index 0000000..51b33d0
--- /dev/null
+++ b/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java
@@ -0,0 +1,158 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.schema;
+
+import org.apache.commons.io.IOUtils;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.logging.CrudServiceMsgs;
+import org.openecomp.crud.util.CrudServiceConstants;
+import org.openecomp.crud.util.FileWatcher;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.ws.rs.core.Response.Status;
+import javax.xml.bind.JAXBException;
+
+
+public class RelationshipSchemaLoader {
+
+ private static Map<String, RelationshipSchema> versionContextMap
+ = new ConcurrentHashMap<String, RelationshipSchema>();
+ private static SortedSet<Integer> versions = new TreeSet<Integer>();
+ private static Map<String, Timer> timers = new ConcurrentHashMap<String, Timer>();
+
+ final static Pattern filePattern = Pattern.compile("aai_relationship_(.*).json");
+
+
+ private static org.openecomp.cl.api.Logger logger = LoggerFactory.getInstance()
+ .getLogger(RelationshipSchemaLoader.class.getName());
+
+ public synchronized static void loadModels() {
+
+ File[] listOfFiles = new File(CrudServiceConstants.CRD_HOME_MODEL).listFiles();
+
+ if (listOfFiles != null) {
+ for (File file : listOfFiles) {
+ if (file.isFile()) {
+ Matcher matcher = filePattern.matcher(file.getName());
+ if (matcher.matches()) {
+ try {
+ RelationshipSchemaLoader.loadModel(matcher.group(1), file);
+ } catch (Exception e) {
+ logger.error(CrudServiceMsgs.INVALID_OXM_FILE, file.getName(), e.getMessage());
+ }
+ }
+
+ }
+ }
+ } else {
+ logger.error(CrudServiceMsgs.INVALID_OXM_DIR, CrudServiceConstants.CRD_HOME_MODEL);
+ }
+
+
+ }
+
+ private static void addtimer(String version, File file) {
+ TimerTask task = null;
+ task = new FileWatcher(
+ file) {
+ protected void onChange(File file) {
+ // here we implement the onChange
+ logger.info(CrudServiceMsgs.OXM_FILE_CHANGED, file.getName());
+
+ try {
+ RelationshipSchemaLoader.loadModel(version, file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ };
+
+ if (!timers.containsKey(version)) {
+ Timer timer = new Timer("aai_relationship_" + version);
+ timer.schedule(task, new Date(), 10000);
+ timers.put(version, timer);
+
+ }
+ }
+
+ private synchronized static void loadModel(String version, File file)
+ throws JAXBException, IOException, CrudException {
+
+ InputStream inputStream = new FileInputStream(file);
+ String content = IOUtils.toString(inputStream, "UTF-8");
+ versionContextMap.put(version, new RelationshipSchema(content));
+ addtimer(version, file);
+ versions.add(Integer.parseInt(version.substring(1)));
+ }
+
+ public static RelationshipSchema getSchemaForVersion(String version) throws CrudException {
+ if (versionContextMap == null || versionContextMap.isEmpty()) {
+ loadModels();
+ } else if (!versionContextMap.containsKey(version)) {
+ try {
+ loadModel(version, new File(CrudServiceConstants.CRD_HOME_MODEL + "aai_relationship_"
+ + version + ".json"));
+ } catch (Exception e) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+ }
+
+ return versionContextMap.get(version);
+ }
+
+ public static String getLatestSchemaVersion() throws CrudException {
+ return "v" + versions.last();
+ }
+
+ public static Map<String, RelationshipSchema> getVersionContextMap() {
+ return versionContextMap;
+ }
+
+ public static void setVersionContextMap(HashMap<String, RelationshipSchema> versionContextMap) {
+ RelationshipSchemaLoader.versionContextMap = versionContextMap;
+ }
+
+ public static void main(String[] args) throws FileNotFoundException, Exception {
+ File initialFile = new File("C:\\Software\\gizmo\\src\\main\\java\\org\\openecomp\\schema\\vio.json");
+
+ loadModel("v8", initialFile);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java b/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java
new file mode 100644
index 0000000..552b60a
--- /dev/null
+++ b/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java
@@ -0,0 +1,341 @@
+/**
+ * ============LICENSE_START=======================================================
+ * Gizmo
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.schema;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+import org.openecomp.crud.service.EdgePayload;
+import org.openecomp.crud.util.CrudServiceUtil;
+import org.radeox.util.logging.Logger;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.ws.rs.core.Response.Status;
+
+public class RelationshipSchemaValidator {
+
+ public static final String SOURCE_NODE = "source";
+ public static final String TARGET_NODE = "target";
+
+ final static Pattern urlPattern = Pattern.compile("services/inventory/(.*)/(.*)/(.*)");
+
+ public static Map<String, Object> resolveCollectionfilter(String version, String type,
+ Map<String, String> filter)
+ throws CrudException {
+
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+ if (schema == null) {
+ throw new CrudException("", Status.NOT_FOUND);
+ }
+
+ HashMap<String, Class<?>> props = schema.lookupRelationType(type);
+ Map<String, Object> result = new HashMap<String, Object>();
+
+ for (String key : filter.keySet()) {
+
+ if (props.containsKey(key)) {
+ try {
+ Object value = CrudServiceUtil.validateFieldType(filter.get(key), props.get(key));
+ result.put(key, value);
+ } catch (Exception ex) {
+ // Skip any exceptions thrown while validating the filter
+ // key value
+ continue;
+ }
+ }
+ }
+
+ return result;
+
+ }
+
+ public static void validateType(String version, String type) throws CrudException {
+
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+ if (!schema.isValidType(type)) {
+ throw new CrudException("Invalid " + RelationshipSchema.SCHEMA_RELATIONSHIP_TYPE
+ + ": " + type,
+ Status.BAD_REQUEST);
+ }
+
+ }
+
+ public static Edge validateIncomingAddPayload(String version, String type, Vertex sourceNode,
+ Vertex targetNode, JsonElement properties)
+ throws CrudException {
+ EdgePayload payload = new EdgePayload();
+ payload.setSource("services/inventory/" + version + "/" + sourceNode.getType()
+ + "/" + sourceNode.getId().get());
+ payload.setTarget("services/inventory/" + version + "/" + targetNode.getType()
+ + "/" + targetNode.getId().get());
+ payload.setType(type);
+ payload.setProperties(properties);
+ return validateIncomingAddPayload(version, type, payload);
+ }
+
+ public static Edge validateIncomingAddPayload(String version, String type, EdgePayload payload)
+ throws CrudException {
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+ try {
+
+ if (payload.getSource() == null || payload.getTarget() == null) {
+ throw new CrudException("Source/Target not specified", Status.BAD_REQUEST);
+ }
+
+ Matcher sourceMatcher = urlPattern.matcher(payload.getSource());
+ Matcher targetMatcher = urlPattern.matcher(payload.getTarget());
+
+ if (!sourceMatcher.matches() || !targetMatcher.matches()) {
+ throw new CrudException("Invalid Source/Target Urls", Status.BAD_REQUEST);
+ }
+
+ // create key based on source:target:relationshipType
+ String sourceNodeType = sourceMatcher.group(2);
+ String targetNodeType = targetMatcher.group(2);
+
+ String sourceNodeId = sourceMatcher.group(3);
+ String targetNodeId = targetMatcher.group(3);
+
+ String key = sourceNodeType + ":" + targetNodeType + ":" + type;
+
+ // find the validate the key from the schema
+ HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+ if (schemaObject == null) {
+ throw new CrudException("Invalid source/target/relationship type: " + key,
+ Status.BAD_REQUEST);
+ }
+
+ Edge.Builder modelEdgeBuilder = new Edge.Builder(type);
+
+ modelEdgeBuilder.source(new Vertex.Builder(sourceNodeType).id(sourceNodeId).build());
+ modelEdgeBuilder.target(new Vertex.Builder(targetNodeType).id(targetNodeId).build());
+
+ // validate it properties
+ validateEdgeProps(modelEdgeBuilder, payload.getProperties(), schemaObject);
+
+ return modelEdgeBuilder.build();
+ } catch (Exception ex) {
+
+ throw new CrudException(ex.getMessage(), Status.BAD_REQUEST);
+ }
+
+ }
+
+ public static Edge validateIncomingPatchPayload(Edge edge, String version, EdgePayload payload)
+ throws CrudException {
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+ try {
+ if (payload.getSource() != null) {
+ Matcher sourceMatcher = urlPattern.matcher(payload.getSource());
+
+ if (!sourceMatcher.matches()) {
+ throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+ }
+ String sourceNodeId = sourceMatcher.group(3);
+ if (!sourceNodeId.equals(edge.getSource().getId().get())) {
+ throw new CrudException("Source can't be updated", Status.BAD_REQUEST);
+ }
+ }
+
+ if (payload.getTarget() != null) {
+ Matcher targetMatcher = urlPattern.matcher(payload.getTarget());
+
+ if (!targetMatcher.matches()) {
+ throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+ }
+ String sourceNodeId = targetMatcher.group(3);
+ if (!sourceNodeId.equals(edge.getTarget().getId().get())) {
+ throw new CrudException("Target can't be updated", Status.BAD_REQUEST);
+ }
+ }
+ // create key based on source:target:relationshipType
+
+ String key = edge.getSource().getType() + ":" + edge.getTarget().getType()
+ + ":" + edge.getType();
+
+ // find the validate the key from the schema
+ HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+ if (schemaObject == null) {
+ Logger.warn("key :" + key
+ + " not found in relationship schema . Skipping the schema validation");
+ return edge;
+ }
+
+ Set<Map.Entry<String, JsonElement>> entries = payload.getProperties()
+ .getAsJsonObject().entrySet();
+
+ for (Map.Entry<String, JsonElement> entry : entries) {
+
+ if (!schemaObject.containsKey(entry.getKey())) {
+ throw new CrudException("Invalid property: " + entry.getKey(), Status.BAD_REQUEST);
+ } else if (entry.getValue() instanceof JsonNull && edge.getProperties()
+ .containsKey(entry.getKey())) {
+ edge.getProperties().remove(entry.getKey());
+ } else if (!(entry.getValue() instanceof JsonNull)) {
+ Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(),
+ schemaObject.get(entry.getKey()));
+ edge.getProperties().put(entry.getKey(), value);
+ }
+
+ }
+
+ return edge;
+
+ } catch (Exception ex) {
+
+ throw new CrudException(ex.getMessage(), Status.BAD_REQUEST);
+ }
+ }
+
+ public static Edge validateIncomingUpdatePayload(Edge edge, String version, Vertex sourceNode,
+ Vertex targetNode, JsonElement properties)
+ throws CrudException {
+ EdgePayload payload = new EdgePayload();
+ payload.setSource("services/inventory/" + version + "/" + sourceNode.getType()
+ + "/" + sourceNode.getId().get());
+ payload.setTarget("services/inventory/" + version + "/" + targetNode.getType()
+ + "/" + targetNode.getId().get());
+ payload.setType(edge.getType());
+ payload.setProperties(properties);
+ return validateIncomingUpdatePayload(edge, version, payload);
+ }
+
+ public static Edge validateIncomingUpdatePayload(Edge edge, String version, EdgePayload payload)
+ throws CrudException {
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+ try {
+
+ if (payload.getSource() != null) {
+ Matcher sourceMatcher = urlPattern.matcher(payload.getSource());
+
+ if (!sourceMatcher.matches()) {
+ throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+ }
+ String sourceNodeId = sourceMatcher.group(3);
+ if (!sourceNodeId.equals(edge.getSource().getId().get())) {
+ throw new CrudException("Source can't be updated", Status.BAD_REQUEST);
+ }
+ }
+
+ if (payload.getTarget() != null) {
+ Matcher targetMatcher = urlPattern.matcher(payload.getTarget());
+
+ if (!targetMatcher.matches()) {
+ throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST);
+ }
+ String sourceNodeId = targetMatcher.group(3);
+ if (!sourceNodeId.equals(edge.getTarget().getId().get())) {
+ throw new CrudException("Target can't be updated", Status.BAD_REQUEST);
+ }
+ }
+ // create key based on source:target:relationshipType
+
+ String key = edge.getSource().getType() + ":" + edge.getTarget().getType()
+ + ":" + edge.getType();
+
+ // find the validate the key from the schema
+ HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+ if (schemaObject == null) {
+ Logger.warn("key :" + key
+ + " not found in relationship schema . Skipping the schema validation");
+ return edge;
+ }
+
+ Edge.Builder updatedEdgeBuilder = new Edge.Builder(edge.getType()).id(edge.getId().get());
+
+ updatedEdgeBuilder
+ .source(new Vertex.Builder(edge.getSource().getType()).id(edge.getSource().getId()
+ .get()).build());
+ updatedEdgeBuilder
+ .target(new Vertex.Builder(edge.getTarget().getType()).id(edge.getTarget().getId()
+ .get()).build());
+
+ validateEdgeProps(updatedEdgeBuilder, payload.getProperties(), schemaObject);
+
+ return updatedEdgeBuilder.build();
+ } catch (Exception ex) {
+
+ throw new CrudException(ex.getMessage(), Status.BAD_REQUEST);
+ }
+ }
+
+
+ private static void validateEdgeProps(Edge.Builder builder, JsonElement props,
+ HashMap<String, Class<?>> schemaObject)
+ throws CrudException {
+ Set<Map.Entry<String, JsonElement>> entries = props.getAsJsonObject().entrySet();
+
+ for (Map.Entry<String, JsonElement> entry : entries) {
+
+ if (!schemaObject.containsKey(entry.getKey())) {
+ throw new CrudException("Invalid property: " + entry.getKey(), Status.BAD_REQUEST);
+ } else {
+ Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(),
+ schemaObject.get(entry.getKey()));
+ builder.property(entry.getKey(), value);
+ }
+
+ }
+
+ }
+
+ public static Edge validateOutgoingPayload(String version, Edge edge) throws CrudException {
+
+ Edge.Builder modelEdgeBuilder = new Edge.Builder(edge.getType()).id(edge.getId()
+ .get()).source(edge.getSource())
+ .target(edge.getTarget());
+
+ RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version);
+
+ String key = edge.getSource().getType() + ":" + edge.getTarget().getType()
+ + ":" + edge.getType();
+ HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key);
+
+ if (schemaObject == null || schemaObject.isEmpty()) {
+ return edge;
+ }
+
+ for (String prop : edge.getProperties().keySet()) {
+ if (schemaObject.containsKey(prop)) {
+ modelEdgeBuilder.property(prop, edge.getProperties().get(prop));
+ }
+
+ }
+ return modelEdgeBuilder.build();
+ }
+
+}
diff --git a/src/main/resources/logging/CrudServiceMsgs.properties b/src/main/resources/logging/CrudServiceMsgs.properties
new file mode 100644
index 0000000..850574f
--- /dev/null
+++ b/src/main/resources/logging/CrudServiceMsgs.properties
@@ -0,0 +1,75 @@
+#Resource key=Error Code|Message text|Resolution text |Description text
+#######
+#Newlines can be utilized to add some clarity ensuring continuing line
+#has atleast one leading space
+#ResourceKey=\
+# CA0000E\
+# Sample error msg txt\
+# Sample resolution msg\
+# Sample description txt
+#
+######
+#Error code classification category
+#000 Info/Debug
+#100 Permission errors
+#200 Availability errors/Timeouts
+#300 Data errors
+#400 Schema Interface type/validation errors
+#500 Business process errors
+#900 Unknown errors
+#
+########################################################################
+
+PROCESS_REST_REQUEST=\
+ CRD0001I|\
+ Received request {0} {1} from {2}. Sending response: {3}|\
+ None. Received the specified REST request from the source specified.|\
+ Received the specified REST request from the source specified, and the CRUD service sent the specified response.
+
+INVALID_OXM_FILE=\
+ CRD0002I|\
+ Unable to parse schema file: {0} due to error : {1}\
+
+OXM_FILE_CHANGED=\
+ CRD0003I|\
+ OXM file change detected: {0}\
+
+STOPPING_CHAMP_DAO=\
+ CRD0004I|\
+ Stopping ChampDAO...
+
+INVALID_OXM_DIR=\
+ CRD0005I|\
+ Invalid OXM dir: {0}\
+
+INSTANTIATE_GRAPH_DAO=\
+ CRD0006I|\
+ Instantiate data access layer for graph data store type: {0} graph: {1} using hosts: {2}
+
+LOADED_OXM_FILE=\
+ CRD0007I|\
+ Successfully loaded schema: {0}
+
+INVALID_GRAPH_BACKEND=\
+ CRD0301E|\
+ Unsupported graph database {0} specified.
+
+INSTANTIATE_GRAPH_BACKEND_ERR=\
+ CRD0302E|\
+ Failure instantiating {0} graph database backend. Cause: {1}
+
+EXCEPTION_DURING_METHOD_CALL=\
+ CRD0502E|\
+ Failed to {0} request for {1} due to: {2}|\
+ Check error cause|\
+ Method failed to execute
+
+OXM_LOAD_ERROR=\
+ CRD0503E|\
+ Unable to load OXM schema: {0}
+
+
+TITAN_GRAPH_INFO=\
+ CRD0504I|\
+ Titan Graph Info: {0}
+ \ No newline at end of file
diff --git a/src/main/runtime/context/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.context b/src/main/runtime/context/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.context
new file mode 100644
index 0000000..8514196
--- /dev/null
+++ b/src/main/runtime/context/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.context
@@ -0,0 +1 @@
+{"context":{"contextClass":"ajsc.Context","contextId":"__module_ajsc_namespace_name__:__module_ajsc_namespace_version__","contextName":"__module_ajsc_namespace_name__","contextVersion":"__module_ajsc_namespace_version__","description":"__module_ajsc_namespace_name__ Context"}} \ No newline at end of file
diff --git a/src/main/runtime/context/default#0.context b/src/main/runtime/context/default#0.context
new file mode 100644
index 0000000..d1b5ab4
--- /dev/null
+++ b/src/main/runtime/context/default#0.context
@@ -0,0 +1 @@
+{"context":{"contextClass":"ajsc.Context","contextId":"default:0","contextName":"default","contextVersion":"0","description":"Default Context"}} \ No newline at end of file
diff --git a/src/main/runtime/deploymentPackage/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.json b/src/main/runtime/deploymentPackage/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.json
new file mode 100644
index 0000000..d0954cf
--- /dev/null
+++ b/src/main/runtime/deploymentPackage/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.json
@@ -0,0 +1 @@
+{"deploymentPackage":{"Class":"ajsc.DeploymentPackage","Id":"__module.ajsc.namespace.name__:__module_ajsc_namespace_version__","namespace":"__module_ajsc_namespace_name__","namespaceVersion":"__module_ajsc_namespace_version__","description":"__module_ajsc_namespace_name__ __module_ajsc_namespace_version__ - default description","userId":"ajsc"}} \ No newline at end of file
diff --git a/src/main/runtime/shiroRole/ajscadmin.json b/src/main/runtime/shiroRole/ajscadmin.json
new file mode 100644
index 0000000..f5e981e
--- /dev/null
+++ b/src/main/runtime/shiroRole/ajscadmin.json
@@ -0,0 +1 @@
+{"shiroRoleClass":"ajsc.auth.ShiroRole","shiroRoleId":"ajscadmin","name":"ajscadmin","permissions":"[ajscadmin:*, ajsc:*]"} \ No newline at end of file
diff --git a/src/main/runtime/shiroRole/contextadmin#__module.ajsc.namespace.name__.json b/src/main/runtime/shiroRole/contextadmin#__module.ajsc.namespace.name__.json
new file mode 100644
index 0000000..2dae9f5
--- /dev/null
+++ b/src/main/runtime/shiroRole/contextadmin#__module.ajsc.namespace.name__.json
@@ -0,0 +1 @@
+{"shiroRoleClass":"ajsc.auth.ShiroRole","shiroRoleId":"contextadmin:__module_ajsc_namespace_name__","name":"contextadmin:__module_ajsc_namespace_name__","permissions":"[]"} \ No newline at end of file
diff --git a/src/main/runtime/shiroRole/contextadmin#default.json b/src/main/runtime/shiroRole/contextadmin#default.json
new file mode 100644
index 0000000..5de814e
--- /dev/null
+++ b/src/main/runtime/shiroRole/contextadmin#default.json
@@ -0,0 +1 @@
+{"shiroRoleClass":"ajsc.auth.ShiroRole","shiroRoleId":"contextadmin:default","name":"contextadmin:default","permissions":"[]"} \ No newline at end of file
diff --git a/src/main/runtime/shiroUser/ajsc.json b/src/main/runtime/shiroUser/ajsc.json
new file mode 100644
index 0000000..f4c7855
--- /dev/null
+++ b/src/main/runtime/shiroUser/ajsc.json
@@ -0,0 +1 @@
+{"shiroUserClass":"ajsc.auth.ShiroUser","shiroUserId":"ajsc","passwordHash":"9471697417008c880720ba54c6038791ad7e98f3b88136fe34f4d31a462dd27a","permissions":"[*:*]","username":"ajsc"} \ No newline at end of file
diff --git a/src/main/runtime/shiroUserRole/ajsc#ajscadmin.json b/src/main/runtime/shiroUserRole/ajsc#ajscadmin.json
new file mode 100644
index 0000000..cb8d483
--- /dev/null
+++ b/src/main/runtime/shiroUserRole/ajsc#ajscadmin.json
@@ -0,0 +1 @@
+{"shiroUserRoleClass":"ajsc.auth.ShiroUserRole","shiroUserRoleId":"ajsc:ajscadmin","roleId":"ajscadmin","userId":"ajsc"} \ No newline at end of file
diff --git a/src/main/runtime/shiroUserRole/ajsc#contextadmin#__module.ajsc.namespace.name__.json b/src/main/runtime/shiroUserRole/ajsc#contextadmin#__module.ajsc.namespace.name__.json
new file mode 100644
index 0000000..95d2361
--- /dev/null
+++ b/src/main/runtime/shiroUserRole/ajsc#contextadmin#__module.ajsc.namespace.name__.json
@@ -0,0 +1 @@
+{"shiroUserRoleClass":"ajsc.auth.ShiroUserRole","shiroUserRoleId":"ajsc:contextadmin:__module_ajsc_namespace_name__","roleId":"contextadmin:__module_ajsc_namespace_name__","userId":"ajsc"} \ No newline at end of file
diff --git a/src/main/runtime/shiroUserRole/ajsc#contextadmin#default.json b/src/main/runtime/shiroUserRole/ajsc#contextadmin#default.json
new file mode 100644
index 0000000..2bd5063
--- /dev/null
+++ b/src/main/runtime/shiroUserRole/ajsc#contextadmin#default.json
@@ -0,0 +1 @@
+{"shiroUserRoleClass":"ajsc.auth.ShiroUserRole","shiroUserRoleId":"ajsc:contextadmin:default","roleId":"contextadmin:default","userId":"ajsc"} \ No newline at end of file
diff --git a/src/test/java/org/openecomp/crud/dao/champ/ChampDaoTest.java b/src/test/java/org/openecomp/crud/dao/champ/ChampDaoTest.java
new file mode 100644
index 0000000..b11e274
--- /dev/null
+++ b/src/test/java/org/openecomp/crud/dao/champ/ChampDaoTest.java
@@ -0,0 +1,624 @@
+package org.openecomp.crud.dao.champ;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.openecomp.crud.dao.GraphDao;
+import org.openecomp.crud.entity.Edge;
+import org.openecomp.crud.entity.Vertex;
+import org.openecomp.crud.exception.CrudException;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * This suite of tests validates the basic functionality of the {@link ChampDao}.
+ */
+public class ChampDaoTest {
+
+ private static final String GRAPH_NAME = "my_test_graph";
+
+ private GraphDao champDao = null;
+
+
+ /**
+ * Perform setup steps that must be done prior to executing each test.
+ */
+ @Before
+ public void setup() {
+
+ // Create an instance of the Champ DAO, backed by the Champ library's in-memory back end
+ // for testing purposes.
+ Properties champDaoProperties = new Properties();
+ champDaoProperties.put(ChampDao.CONFIG_STORAGE_BACKEND, "in-memory");
+ champDaoProperties.put(ChampDao.CONFIG_GRAPH_NAME, GRAPH_NAME);
+ champDao = new ChampDao(champDaoProperties);
+ }
+
+
+ /**
+ * Perform tear down steps that must be done after executing each test.
+ */
+ @After
+ public void tearDown() {
+
+ // Release the Champ DAO instance that we were using for the test.
+ if (champDao != null) {
+ ((ChampDao) champDao).close();
+ }
+ }
+
+
+ /**
+ * Tests the ability of the {@link ChampDao} to create a vertex.
+ *
+ * @throws CrudException
+ */
+ @Test
+ public void createVertexTest() throws CrudException {
+
+ String VERTEX_TYPE = "Test_Vertex";
+
+ Map<String, Object> properties = new HashMap<String, Object>();
+ properties.put("property1", "something");
+ properties.put("property2", "something else");
+
+ // Create the vertex.
+ Vertex createdVertex = champDao.addVertex(VERTEX_TYPE, properties);
+
+ // Validate that the returned {@link Vertex} has the right label assigned to it.
+ assertTrue("Unexpected vertex type '" + createdVertex.getType() + "' returned from DAO",
+ createdVertex.getType().equals(VERTEX_TYPE));
+
+ // Validate that all of the properties that we provided to the DAO are in fact assigned
+ // to the {@link Vertex} that we got back.
+ assertTrue("Vertex property list returned from DAO did not contain all expected properties - expected: " +
+ properties.keySet() + " actual: " + createdVertex.getProperties().keySet(),
+ createdVertex.getProperties().keySet().containsAll(properties.keySet()));
+
+ // Validate that the values assigned to the properties in the returned {@link Vertex}
+ // match the ones that we provided.
+ for (String propertyKey : properties.keySet()) {
+
+ assertTrue(createdVertex.getProperties().get(propertyKey).equals(properties.get(propertyKey)));
+ }
+ }
+
+
+ /**
+ * Tests the ability of the {@link ChampDao} to retrieve a vertex from the graph data store
+ * by its unique identifier.
+ *
+ * @throws CrudException
+ */
+ @Test
+ public void getVertexByIdTest() throws CrudException {
+
+ String VERTEX_TYPE = "Test_Vertex";
+
+ Map<String, Object> properties = new HashMap<String, Object>();
+ properties.put("property1", "something");
+ properties.put("property2", "something else");
+
+ // Create the vertex.
+ Vertex createdVertex = champDao.addVertex(VERTEX_TYPE, properties);
+
+ // Make sure the {@link Vertex} returned from the create method includes an id that we can
+ // use to retrieve it.
+ assertTrue("No valid id returned for the created vertex", createdVertex.getId().isPresent());
+
+ // Now, retrieve the {@link Vertex} by its identifier.
+ Vertex retrievedVertex = champDao.getVertex(createdVertex.getId().get(), VERTEX_TYPE);
+
+ // Validate that the retrieved {@link Vertex} has the right label assigned to it.
+ assertTrue("Unexpected vertex type '" + retrievedVertex.getType() + "' returned from DAO",
+ retrievedVertex.getType().equals(VERTEX_TYPE));
+
+ // Validate that all of the properties that we provided when we created the {@link Vertex}
+ // are present in the {@link Vertex} that we retrieved.
+ assertTrue("Vertex property list returned from DAO did not contain all expected properties - expected: " +
+ properties.keySet() + " actual: " + retrievedVertex.getProperties().keySet(),
+ retrievedVertex.getProperties().keySet().containsAll(properties.keySet()));
+
+ // Validate that the values assigned to the properties in the retrieved {@link Vertex}
+ // match the ones that we provided when we created it.
+ for (String propertyKey : properties.keySet()) {
+
+ assertTrue(retrievedVertex.getProperties().get(propertyKey).equals(properties.get(propertyKey)));
+ }
+ }
+
+
+ /**
+ * Tests the ability of the {@link ChampDao} to update an already existing vertex.
+ *
+ * @throws CrudException
+ */
+ @Test
+ public void updateVertexTest() throws CrudException {
+
+ final String VERTEX_TYPE = "Test_Vertex";
+
+ Map<String, Object> properties = new HashMap<String, Object>();
+ properties.put("property1", "something");
+ properties.put("property2", "something else");
+
+ // Create the vertex.
+ Vertex createdVertex = champDao.addVertex(VERTEX_TYPE, properties);
+
+ // Make sure the {@link Vertex} returned from the create method includes an id that we can
+ // use to retrieve it.
+ assertTrue("No valid id returned for the created vertex", createdVertex.getId().isPresent());
+
+ // Modify the properties list...
+ properties.put("property3", "a new property");
+ properties.remove("property1");
+
+ // ...and apply it to our vertex.
+ Vertex updatedVertex = champDao.updateVertex(createdVertex.getId().get(), createdVertex.getType(), properties);
+
+ assertTrue("Vertex property list returned from DAO update operation did not contain all expected properties - expected: " +
+ properties.keySet() + " actual: " + updatedVertex.getProperties().keySet(),
+ updatedVertex.getProperties().keySet().containsAll(properties.keySet()));
+
+ // Validate that the values assigned to the properties in the updated {@link Vertex}
+ // match the ones that we provided when we created it.
+ for (String propertyKey : properties.keySet()) {
+
+ assertTrue("Unexpected value for property '" + propertyKey + "' - Expected: " +
+ properties.get(propertyKey) + " Actual: " +
+ updatedVertex.getProperties().get(propertyKey),
+ updatedVertex.getProperties().get(propertyKey).equals(properties.get(propertyKey)));
+ }
+
+ // Validate that the property that we removed is NOT in the set of properties from our
+ // updated {@link Vertex}.
+ assertFalse("Property 'property1' should no longer be associated with updated vertex",
+ updatedVertex.getProperties().containsKey("property1"));
+ }
+
+
+ /**
+ * Tests the ability of the {@link ChampDao} to retrieve multiple vertices which match
+ * a particular set of supplied properties.
+ *
+ * @throws CrudException
+ */
+ @Test
+ public void getVerticesTest() throws CrudException {
+
+ final String FIRST_VERTEX_TYPE = "pserver";
+ final String SECOND_VERTEX_TYPE = "complex";
+
+ // Create some vertices.
+
+ Map<String, Object> vertex1Properties = new HashMap<String, Object>();
+ vertex1Properties.put("O/S", "Linux");
+ vertex1Properties.put("version", "6.5");
+ vertex1Properties.put("hostname", "kll0001");
+ champDao.addVertex(FIRST_VERTEX_TYPE, vertex1Properties);
+
+ Map<String, Object> vertex2Properties = new HashMap<String, Object>();
+ vertex2Properties.put("O/S", "Linux");
+ vertex2Properties.put("version", "6.5");
+ vertex2Properties.put("hostname", "kll0002");
+ champDao.addVertex(FIRST_VERTEX_TYPE, vertex2Properties);
+
+ Map<String, Object> vertex3Properties = new HashMap<String, Object>();
+ vertex3Properties.put("O/S", "Linux");
+ vertex3Properties.put("version", "7.2");
+ vertex3Properties.put("hostname", "kll0003");
+ champDao.addVertex(FIRST_VERTEX_TYPE, vertex3Properties);
+
+ Map<String, Object> vertex4Properties = new HashMap<String, Object>();
+ vertex4Properties.put("O/S", "Windows");
+ vertex4Properties.put("version", "10");
+ vertex4Properties.put("hostname", "Dev Laptop");
+ champDao.addVertex(FIRST_VERTEX_TYPE, vertex4Properties);
+
+ Map<String, Object> vertex5Properties = new HashMap<String, Object>();
+ vertex5Properties.put("Street", "Baker");
+ vertex5Properties.put("Number", "222B");
+ champDao.addVertex(SECOND_VERTEX_TYPE, vertex5Properties);
+
+ // Create a set of properties to use for our query.
+ Map<String, Object> queryProperties = new HashMap<String, Object>();
+ queryProperties.put("O/S", "Linux");
+ queryProperties.put("version", "6.5");
+
+ // Validate that we filter our 'get vertices' results by type
+ List<Vertex> allVerticesByType = champDao.getVertices(FIRST_VERTEX_TYPE, MapBuilder.builder().build());
+ for (Vertex v : allVerticesByType) {
+ assertTrue("Unexpected vertex type returned from query. Expected: " +
+ FIRST_VERTEX_TYPE + " Actual: " + v.getType(),
+ v.getType().equals(FIRST_VERTEX_TYPE));
+ }
+
+ // Now, request the vertices that match our parameters.
+ List<Vertex> vertices = champDao.getVertices(FIRST_VERTEX_TYPE, queryProperties);
+
+ // Validate that got back the expected number of vertices.
+ assertEquals(vertices.size(), 2);
+
+ // Validate that the vertices we got back contain the expected parameters.
+ for (Vertex v : vertices) {
+
+ assertTrue("Vertex from query result does not contain expected vertex 'O/S'",
+ v.getProperties().containsKey("O/S"));
+ assertTrue("Vertex from query result contains unexpected value for 'O/S' parameter - Expected: 'Linux' Actual: '" +
+ v.getProperties().get("O/S") + "'",
+ v.getProperties().get("O/S").equals("Linux"));
+
+ assertTrue("Vertex from query result does not contain expected vertex 'O/S'",
+ v.getProperties().containsKey("version"));
+ assertTrue("Vertex from query result contains unexpected value for 'O/S' parameter - Expected: 'Linux' Actual: '" +
+ v.getProperties().get("O/S") + "'",
+ v.getProperties().get("version").equals("6.5"));
+ }
+ }
+
+ @Test
+ public void deleteVertexTest() throws CrudException {
+
+ boolean deletedVertexNotFound = false;
+
+ // Create a vertex.
+ Vertex createdVertex = champDao.addVertex("test_type", MapBuilder.builder()
+ .withKeyValue("O/S", "Linux")
+ .withKeyValue("version", "6.5")
+ .withKeyValue("hostname", "kll0001")
+ .build());
+
+ // Verify that we can retrieve the vertex from the graph data base.
+ Vertex retrievedVertex = champDao.getVertex(createdVertex.getId().get(), "test_type");
+
+ // Now, delete the vertex.
+ champDao.deleteVertex(createdVertex.getId().get(), "test_type");
+
+ // Now, try to retrieve it again. This time we should fail to find it.
+ try {
+ champDao.getVertex(createdVertex.getId().get(), "test_type");
+
+ } catch (CrudException e) {
+ assertTrue(e.getMessage().contains("No vertex with id"));
+ deletedVertexNotFound = true;
+ }
+
+ assertTrue("Should not have been able to retrieve deleted vertex", deletedVertexNotFound);
+ }
+
+ @Test
+ public void createEdgeTest() throws CrudException {
+
+ String EDGE_TYPE = "has";
+
+ // Create the source vertex for the edge.
+ Map<String, Object> srcVertexProperties = new HashMap<String, Object>();
+ srcVertexProperties.put("O/S", "Linux");
+ srcVertexProperties.put("version", "6.5");
+ srcVertexProperties.put("hostname", "kll0001");
+ Vertex sourceVertex = champDao.addVertex("vserver", srcVertexProperties);
+
+ // Create the target vertex for the edge.
+ Map<String, Object> dstVertexProperties = new HashMap<String, Object>();
+ dstVertexProperties.put("O/S", "Linux");
+ dstVertexProperties.put("version", "6.5");
+ dstVertexProperties.put("hostname", "kll0002");
+ Vertex destVertex = champDao.addVertex("VNF", dstVertexProperties);
+
+ // Now, create the edge itself.
+ Map<String, Object> edgeProperties = new HashMap<String, Object>();
+ edgeProperties.put("prop", "val");
+ Edge createdEdge = champDao.addEdge("has", sourceVertex, destVertex, edgeProperties);
+
+ // Validate that the Edge object returned from the create method matches what we were
+ // trying to create.
+ assertTrue("Unexpected type for Edge returned from create method. Expected: " + EDGE_TYPE
+ + " Actual: " + createdEdge.getType(),
+ createdEdge.getType().equals("has"));
+ assertTrue("Unexpected properties for Edge returned from create method. Expected: " + edgeProperties
+ + " Actual: " + createdEdge.getProperties(),
+ createdEdge.getProperties().equals(edgeProperties));
+
+ }
+
+ @Test
+ public void createEdgeWithMissingSrcOrTargetTest() throws CrudException {
+
+ String EDGE_TYPE = "has";
+
+ // Create the source vertex for the edge.
+ Map<String, Object> srcVertexProperties = new HashMap<String, Object>();
+ srcVertexProperties.put("O/S", "Linux");
+ srcVertexProperties.put("version", "6.5");
+ srcVertexProperties.put("hostname", "kll0001");
+ Vertex sourceVertex = champDao.addVertex("vserver", srcVertexProperties);
+
+ // Create the target vertex for the edge.
+ Map<String, Object> dstVertexProperties = new HashMap<String, Object>();
+ dstVertexProperties.put("O/S", "Linux");
+ dstVertexProperties.put("version", "6.5");
+ dstVertexProperties.put("hostname", "kll0002");
+ Vertex destVertex = champDao.addVertex("VNF", dstVertexProperties);
+
+ // Now, try creating the Edge but specify an id for the source vertex that does
+ // not exist.
+ Map<String, Object> edgeProperties = new HashMap<String, Object>();
+ edgeProperties.put("prop", "val");
+ try {
+ champDao.addEdge(EDGE_TYPE, new Vertex.Builder("miss").id("99").build(), destVertex, edgeProperties);
+ } catch (CrudException e) {
+ assertTrue(e.getMessage().contains("Error creating edge - source vertex"));
+ }
+
+ // Now, try created the Edge with a valid source vertex, but specify an id for the
+ // target vertex that does not exist.
+ try {
+ champDao.addEdge(EDGE_TYPE, sourceVertex, new Vertex.Builder("miss").id("99").build(), edgeProperties);
+ } catch (CrudException e) {
+ assertTrue(e.getMessage().contains("Error creating edge - target vertex"));
+ }
+
+ }
+
+ @Test
+ public void getEdgeByIdTest() throws CrudException {
+
+ String EDGE_TYPE = "has";
+
+ // Create the source vertex for the edge.
+ Map<String, Object> srcVertexProperties = new HashMap<String, Object>();
+ srcVertexProperties.put("O/S", "Linux");
+ srcVertexProperties.put("version", "6.5");
+ srcVertexProperties.put("hostname", "kll0001");
+ Vertex sourceVertex = champDao.addVertex("vserver", srcVertexProperties);
+
+ // Create the target vertex for the edge.
+ Map<String, Object> dstVertexProperties = new HashMap<String, Object>();
+ dstVertexProperties.put("O/S", "Linux");
+ dstVertexProperties.put("version", "6.5");
+ dstVertexProperties.put("hostname", "kll0002");
+ Vertex destVertex = champDao.addVertex("VNF", dstVertexProperties);
+
+ // Now, create the edge itself.
+ Map<String, Object> edgeProperties = new HashMap<String, Object>();
+ edgeProperties.put("prop", "val");
+ Edge createdEdge = champDao.addEdge("has", sourceVertex, destVertex, edgeProperties);
+
+ // Retrieve the edge we just created by specifying its unique identifier.
+ Edge retrievedEdge = champDao.getEdge(createdEdge.getId().get(), "has");
+
+ // Validate that the contents of the object that we got back matches what we thought we
+ // created.
+ assertTrue("Unexpected type for Edge returned from get method. Expected: " + EDGE_TYPE
+ + " Actual: " + retrievedEdge.getType(),
+ retrievedEdge.getType().equals(EDGE_TYPE));
+ assertTrue("Unexpected properties for Edge returned from get method. Expected: " + edgeProperties
+ + " Actual: " + retrievedEdge.getProperties(),
+ retrievedEdge.getProperties().equals(edgeProperties));
+ }
+
+ @Test
+ public void getEdgesTest() throws CrudException {
+
+ final String EDGE_TYPE_HAS = "has";
+ final String EDGE_TYPE_RUNS = "runs";
+
+ // Create some vertices and edges that we can query agains.
+ Vertex complex = champDao.addVertex("complex", MapBuilder.builder()
+ .withKeyValue("Province", "Ontario")
+ .withKeyValue("City", "Ottawa")
+ .withKeyValue("Street", "303 Terry Fox")
+ .build());
+
+ Vertex vserver = champDao.addVertex("vserver", MapBuilder.builder()
+ .withKeyValue("O/S", "Linux")
+ .withKeyValue("version", "6.5")
+ .withKeyValue("hostname", "kll0001")
+ .build());
+
+ Vertex vnf1 = champDao.addVertex("vserver", MapBuilder.builder()
+ .withKeyValue("Application", "OpenDaylight")
+ .build());
+
+ Vertex vnf2 = champDao.addVertex("vserver", MapBuilder.builder()
+ .withKeyValue("Application", "Cammunda")
+ .build());
+
+ Edge edge1 = champDao.addEdge(EDGE_TYPE_HAS, complex, vserver,
+ MapBuilder.builder()
+ .withKeyValue("usesResource", "false")
+ .withKeyValue("hasDelTarget", "false")
+ .build());
+
+ Edge edge2 = champDao.addEdge(EDGE_TYPE_RUNS, vserver, vnf1,
+ MapBuilder.builder()
+ .withKeyValue("usesResource", "false")
+ .withKeyValue("hasDelTarget", "true")
+ .build());
+
+ Edge edge3 = champDao.addEdge(EDGE_TYPE_RUNS, vserver, vnf2,
+ MapBuilder.builder()
+ .withKeyValue("usesResource", "false")
+ .withKeyValue("hasDelTarget", "false")
+ .build());
+
+ // Query for all HAS edges.
+ List<Edge> hasEdges = champDao.getEdges(EDGE_TYPE_HAS, new HashMap<String, Object>());
+
+ assertEquals("Unexpected number of edges of type 'has' found. Expected: 1 Actual: " + hasEdges.size(),
+ hasEdges.size(), 1);
+ assertTrue("Result of query for 'has' type edges does not contain the expected results",
+ containsEdge(edge1, hasEdges));
+
+ // Query for all RUNS edges.
+ List<Edge> runsEdges = champDao.getEdges(EDGE_TYPE_RUNS, new HashMap<String, Object>());
+
+ assertEquals("Unexpected number of edges of type 'runs' found. Expected: 2 Actual: " + runsEdges.size(),
+ runsEdges.size(), 2);
+ assertTrue("Result of query for 'runs' type edges does not contain the expected results",
+ containsEdge(edge2, runsEdges));
+ assertTrue("Result of query for 'runs' type edges does not contain the expected results",
+ containsEdge(edge2, runsEdges));
+
+ // Query for all HAS edges with the property 'hasDelTarget' equal to 'true'.
+ List<Edge> runsEdgesWithDelTargetTrue =
+ champDao.getEdges(EDGE_TYPE_RUNS, MapBuilder.builder()
+ .withKeyValue("hasDelTarget", "true")
+ .build());
+
+ assertEquals("Unexpected number of edges of type 'has' with 'hasDelTarget=true' found. Expected: 1 Actual: "
+ + runsEdgesWithDelTargetTrue.size(),
+ runsEdgesWithDelTargetTrue.size(), 1);
+ assertTrue("Result of query for 'runs' type edges with delTarget set to TRUE does not contain the expected results",
+ containsEdge(edge2, runsEdgesWithDelTargetTrue));
+ }
+
+ @Test
+ @Ignore // For now - pending some expected fixes to the Champ library.
+ public void updateEdgeTest() throws CrudException {
+
+ // Create the source vertex for the edge.
+ Vertex sourceVertex = champDao.addVertex("vserver", MapBuilder.builder()
+ .withKeyValue("O/S", "Linux")
+ .withKeyValue("version", "6.5")
+ .withKeyValue("hostname", "kll0001")
+ .build());
+
+ // Create the target vertex for the edge.
+ Vertex destVertex = champDao.addVertex("VNF", MapBuilder.builder()
+ .withKeyValue("O/S", "Linux")
+ .withKeyValue("version", "6.5")
+ .withKeyValue("hostname", "kll0002")
+ .build());
+
+ // Now, create the edge itself.
+ Edge createdEdge = champDao.addEdge("has",
+ sourceVertex,
+ destVertex,
+ MapBuilder.builder()
+ .withKeyValue("key1", "value1")
+ .withKeyValue("key2", "value2")
+ .withKeyValue("key3", "value3")
+ .build());
+
+ // Make sure the Edge returned from the create method includes an id that we can
+ // use to retrieve it.
+ assertTrue("No valid id returned for the created edge", createdEdge.getId().isPresent());
+
+ // Retrieve the properties map for our edge and make some changes.
+ Map<String, Object> properties = createdEdge.getProperties();
+ properties.put("key4", "value4");
+ properties.remove("key2");
+
+ // Now update the edge with the new properties map.
+ Edge updatedEdge = champDao.updateEdge(createdEdge);
+
+ assertTrue("Edge property list returned from DAO update operation did not contain all expected properties - expected: " +
+ properties.keySet() + " actual: " + updatedEdge.getProperties().keySet(),
+ updatedEdge.getProperties().keySet().containsAll(properties.keySet()));
+
+ // Validate that the values assigned to the properties in the updated Edge
+ // match the ones that we provided when we created it.
+ for (String propertyKey : properties.keySet()) {
+
+ assertTrue("Unexpected value for property '" + propertyKey + "' - Expected: " +
+ properties.get(propertyKey) + " Actual: " +
+ updatedEdge.getProperties().get(propertyKey),
+ updatedEdge.getProperties().get(propertyKey).equals(properties.get(propertyKey)));
+ }
+
+ // Validate that the property that we removed is NOT in the set of properties from our
+ // updated edge.
+ // *** We will leave this validation commented out for now, as the Champ library actually
+ // merges update properties instead of replacing them...
+// assertFalse("Property 'key2' should no longer be associated with updated edge",
+// updatedEdge.getProperties().containsKey("key2"));
+ }
+
+ @Test
+ public void deleteEdgeTest() throws CrudException {
+
+ boolean deletedEdgeNotFound = false;
+
+ // Create the source vertex for the edge.
+ Vertex sourceVertex = champDao.addVertex("vserver", MapBuilder.builder()
+ .withKeyValue("O/S", "Linux")
+ .withKeyValue("version", "6.5")
+ .withKeyValue("hostname", "kll0001")
+ .build());
+
+ // Create the target vertex for the edge.
+ Vertex destVertex = champDao.addVertex("VNF", MapBuilder.builder()
+ .withKeyValue("O/S", "Linux")
+ .withKeyValue("version", "6.5")
+ .withKeyValue("hostname", "kll0002")
+ .build());
+
+ // Now, create the edge itself.
+ Edge createdEdge = champDao.addEdge("has",
+ sourceVertex,
+ destVertex,
+ MapBuilder.builder()
+ .withKeyValue("key1", "value1")
+ .withKeyValue("key2", "value2")
+ .withKeyValue("key3", "value3")
+ .build());
+
+ // Verify that we can retrieve the edge that we just created.
+ Edge retrievedEdge = champDao.getEdge(createdEdge.getId().get(), "has");
+
+ // Now, delete it.
+ champDao.deleteEdge(createdEdge.getId().get(), "has");
+
+ // Try retrieving it again. This time we should not find it.
+ try {
+ champDao.getEdge(createdEdge.getId().get(), "has");
+ } catch (CrudException e) {
+
+ assertTrue(e.getMessage().contains("No edge with id"));
+ deletedEdgeNotFound = true;
+ }
+
+ assertTrue("Should not have been able to retrieve deleted edge.", deletedEdgeNotFound);
+ }
+
+ private boolean containsEdge(Edge anEdge, List<Edge> edges) {
+
+ for (Edge e : edges) {
+ if (e.getId().isPresent() && anEdge.getId().isPresent() && (e.getId().get().equals(anEdge.getId().get()))) {
+ return true;
+ }
+
+ }
+ return false;
+ }
+
+ public static class MapBuilder {
+
+ private Map<String, Object> map;
+
+ private MapBuilder() {
+ map = new HashMap<String, Object>();
+ }
+
+ public static MapBuilder builder() {
+ return new MapBuilder();
+ }
+
+ public MapBuilder withKeyValue(String key, Object value) {
+ map.put(key, value);
+ return this;
+ }
+
+ public Map<String, Object> build() {
+ return map;
+ }
+ }
+}
diff --git a/src/test/java/org/openecomp/schema/OxmModelLoaderTest.java b/src/test/java/org/openecomp/schema/OxmModelLoaderTest.java
new file mode 100644
index 0000000..bf4d871
--- /dev/null
+++ b/src/test/java/org/openecomp/schema/OxmModelLoaderTest.java
@@ -0,0 +1,22 @@
+package org.openecomp.schema;
+
+import org.junit.Test;
+import org.openecomp.crud.exception.CrudException;
+
+import static org.junit.Assert.assertTrue;
+
+
+public class OxmModelLoaderTest {
+
+ @Test
+ public void loadModels() {
+ try {
+ OxmModelLoader.loadModels();
+ } catch (CrudException e) {
+ e.printStackTrace();
+ assertTrue(false);
+ }
+
+ assertTrue(OxmModelLoader.getVersionContextMap().size() == 4);
+ }
+}
diff --git a/src/test/resources/gremlin/gremlinResponseEmptyVertex.json b/src/test/resources/gremlin/gremlinResponseEmptyVertex.json
new file mode 100644
index 0000000..0e4b9f6
--- /dev/null
+++ b/src/test/resources/gremlin/gremlinResponseEmptyVertex.json
@@ -0,0 +1,15 @@
+{
+ "requestId": "54275f43-4cf5-491d-ad53-a9dada9c0fbf",
+ "status": {
+ "message": "",
+ "code": 200,
+ "attributes": {
+ }
+ },
+ "result": {
+ "data": [
+ ],
+ "meta": {
+ }
+ }
+} \ No newline at end of file
diff --git a/src/test/resources/gremlin/gremlinResponseMultipleVertex.json b/src/test/resources/gremlin/gremlinResponseMultipleVertex.json
new file mode 100644
index 0000000..5809be3
--- /dev/null
+++ b/src/test/resources/gremlin/gremlinResponseMultipleVertex.json
@@ -0,0 +1,108 @@
+{
+ "requestId":"54275f43-4cf5-491d-ad53-a9dada9c0fbf",
+ "status":{
+ "message":"",
+ "code":200,
+ "attributes":{
+ }
+ },
+ "result":{
+ "data":[
+ {
+ "id":100,
+ "label":"vertex",
+ "type":"vertex",
+ "properties":{
+ "aai-last-mod-ts":[
+ {
+ "id":"j2mu-cwq8-sl",
+ "value":1477013842
+ }
+ ],
+ "speed-value":[
+ {
+ "id":"j312-cwq8-rnp",
+ "value":"50000000"
+ }
+ ],
+ "source-of-truth":[
+ {
+ "id":"j5ee-cwq8-6bp",
+ "value":"SOT2"
+ }
+ ],
+ "aai-node-type":[
+ {
+ "id":"j5sm-cwq8-8p1",
+ "value":"physical-link"
+ }
+ ]
+ }
+ },
+ {
+ "id":200,
+ "label":"vertex",
+ "type":"vertex",
+ "properties":{
+ "aai-last-mod-ts":[
+ {
+ "id":"j2mu-cwq8-sl",
+ "value":1477013842
+ }
+ ],
+ "speed-value":[
+ {
+ "id":"j312-cwq8-rnp",
+ "value":"10000000"
+ }
+ ],
+ "source-of-truth":[
+ {
+ "id":"j5ee-cwq8-6bp",
+ "value":"RCT"
+ }
+ ],
+ "aai-node-type":[
+ {
+ "id":"j5sm-cwq8-8p1",
+ "value":"physical-link"
+ }
+ ]
+ }
+ },
+ {
+ "id":300,
+ "label":"vertex",
+ "type":"vertex",
+ "properties":{
+ "aai-last-mod-ts":[
+ {
+ "id":"j2mu-cwq8-sl",
+ "value":1477013842
+ }
+ ],
+ "speed-value":[
+ {
+ "id":"j312-cwq8-rnp",
+ "value":"9999"
+ }
+ ],
+ "source-of-truth":[
+ {
+ "id":"j5ee-cwq8-6bp",
+ "value":"RCT"
+ }
+ ],
+ "aai-node-type":[
+ {
+ "id":"j5sm-cwq8-8p1",
+ "value":"physical-link"
+ }
+ ]
+ }
+ }
+ ],
+ "meta":{
+ }
+ }
+} \ No newline at end of file
diff --git a/src/test/resources/gremlin/gremlinResponseVertex.json b/src/test/resources/gremlin/gremlinResponseVertex.json
new file mode 100644
index 0000000..2e3a290
--- /dev/null
+++ b/src/test/resources/gremlin/gremlinResponseVertex.json
@@ -0,0 +1,76 @@
+{
+ "requestId":"54275f43-4cf5-491d-ad53-a9dada9c0fbf",
+ "status":{
+ "message":"",
+ "code":200,
+ "attributes":{
+ }
+ },
+ "result":{
+ "data":[
+ {
+ "id":602288,
+ "label":"vertex",
+ "type":"vertex",
+ "properties":{
+ "aai-last-mod-ts":[
+ {
+ "id":"j2mu-cwq8-sl",
+ "value":1477013842
+ }
+ ],
+ "speed-value":[
+ {
+ "id":"j312-cwq8-rnp",
+ "value":"10000000"
+ }
+ ],
+ "link-name":[
+ {
+ "id":"j3fa-cwq8-u11",
+ "value":"ldouk116sd9:vmnic11-6/2|ldouktax102:xe-1/0/66"
+ }
+ ],
+ "resource-version":[
+ {
+ "id":"j3ti-cwq8-1l1",
+ "value":"1477013842"
+ }
+ ],
+ "speed-units":[
+ {
+ "id":"j47q-cwq8-t8l",
+ "value":"kbps"
+ }
+ ],
+ "aai-created-ts":[
+ {
+ "id":"j4ly-cwq8-4qt",
+ "value":1476927322
+ }
+ ],
+ "last-mod-source-of-truth":[
+ {
+ "id":"j506-cwq8-5j9",
+ "value":"RCT"
+ }
+ ],
+ "source-of-truth":[
+ {
+ "id":"j5ee-cwq8-6bp",
+ "value":"RCT"
+ }
+ ],
+ "aai-node-type":[
+ {
+ "id":"j5sm-cwq8-8p1",
+ "value":"physical-link"
+ }
+ ]
+ }
+ }
+ ],
+ "meta":{
+ }
+ }
+} \ No newline at end of file