ETags on resources 67/54967/3
authorSotiropoulos, Ioannis (is948x) <Ioannis.Sotiropoulos@amdocs.com>
Fri, 15 Jun 2018 14:32:01 +0000 (15:32 +0100)
committerMichael Arrastia <MArrasti@amdocs.com>
Mon, 18 Jun 2018 10:33:18 +0000 (11:33 +0100)
An etag should be generated (by Champ) when creating
a resource (edge or vertex). The Champ microservice should return
this etag in the response header. Gizmo should also return the etag
in it's response header (Gizmo will receive the etag from Champ).

Issue-ID: AAI-1196

Change-Id: Ie16f871eccbceeccde037e73e0de0d96eeba18bd
Signed-off-by: Sotiropoulos, Ioannis (is948x) <Ioannis.Sotiropoulos@amdocs.com>
20 files changed:
.gitignore
src/main/java/org/onap/crud/dao/GraphDao.java
src/main/java/org/onap/crud/dao/champ/ChampDao.java
src/main/java/org/onap/crud/event/GraphEvent.java
src/main/java/org/onap/crud/event/envelope/GraphEventHeader.java
src/main/java/org/onap/crud/parser/CrudResponseBuilder.java
src/main/java/org/onap/crud/service/AaiResourceService.java
src/main/java/org/onap/crud/service/AbstractGraphDataService.java
src/main/java/org/onap/crud/service/CrudAsyncGraphDataService.java
src/main/java/org/onap/crud/service/CrudGraphDataService.java
src/main/java/org/onap/crud/service/CrudRestService.java
src/main/java/org/onap/crud/service/EdgePayload.java
src/main/java/org/onap/crud/service/VertexPayload.java
src/main/java/org/onap/crud/util/CrudServiceConstants.java
src/main/java/org/onap/crud/util/CrudServiceUtil.java
src/main/java/org/onap/crud/util/HashGenerator.java [new file with mode: 0644]
src/main/java/org/onap/crud/util/etag/EtagGenerator.java [new file with mode: 0644]
src/test/java/org/onap/crud/service/CrudRestServiceTest.java
src/test/java/org/onap/crud/service/TestDao.java [moved from src/test/java/org/onap/crud/dao/TestDao.java with 56% similarity]
src/test/java/org/onap/crud/util/etag/EtagGeneratorTest.java [new file with mode: 0644]

index 88cf0cb..3615915 100644 (file)
@@ -14,3 +14,7 @@ target/
 # IntelliJ
 .idea/
 *.iml
+
+# Misc
+.checkstyle
+
index 29ea6da..fe638ce 100644 (file)
@@ -23,9 +23,8 @@ package org.onap.crud.dao;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-
+import org.onap.aai.restclient.client.OperationResult;
 import org.onap.crud.entity.Edge;
-
 import org.onap.crud.entity.Vertex;
 import org.onap.crud.exception.CrudException;
 
@@ -33,7 +32,7 @@ public interface GraphDao {
 
   public Vertex getVertex(String id, String version) throws CrudException;
 
-  public Vertex getVertex(String id, String type, String version, Map<String, String> queryParams) throws CrudException;
+  public OperationResult getVertex(String id, String type, String version, Map<String, String> queryParams) throws CrudException;
 
   /**
    * Retrieve all of the edges which are incident to the vertex with the
@@ -42,7 +41,7 @@ public interface GraphDao {
    * @param id
    *          - The unique identifier of the vertex to retrieve the edges for.
    * @param queryParams
-   *             - query parameters to be passed         
+   *             - query parameters to be passed
    * @return - A collection of edges.
    * @throws CrudException
    */
@@ -56,10 +55,10 @@ public interface GraphDao {
    *          - The vertex type that we want to retrieve.
    * @param filter
    *          - The parameters to filter our results by.
-   * @return - A collection of vertices.
+   * @return - The {@link OperationResult} OperationResult
    * @throws CrudException
    */
-  public List<Vertex> getVertices(String type, Map<String, Object> filter, String version) throws CrudException;
+  public OperationResult getVertices(String type, Map<String, Object> filter, String version) throws CrudException;
 
   /**
    * Retrieve a collection of {@link Vertex} objects which match the supplied
@@ -71,10 +70,10 @@ public interface GraphDao {
    *          - The parameters to filter our results by.
    * @param properties
    *          - The properties to retrieve with the vertex
-   * @return - A collection of vertices.
+   * @return - The {@link OperationResult} OperationResult
    * @throws CrudException
    */
-  public List<Vertex> getVertices(String type, Map<String, Object> filter, HashSet<String> properties, String version) throws CrudException;
+  public OperationResult getVertices(String type, Map<String, Object> filter, HashSet<String> properties, String version) throws CrudException;
 
   /**
    * Retrieve an {@link Edge} from the graph database by specifying its unique
@@ -85,11 +84,11 @@ public interface GraphDao {
    * @param type
    *          - The type that we want to retrieve.
    * @param queryParams
-   *             - query parameters to be passed                
-   * @return - The Edge corresponding to the specified identifier.
+   *             - query parameters to be passed
+   * @return - The {@link OperationResult} OperationResult corresponding to the specified identifier.
    * @throws CrudException
    */
-  public Edge getEdge(String id, String type, Map<String, String> queryParams) throws CrudException;
+  public OperationResult getEdge(String id, String type, Map<String, String> queryParams) throws CrudException;
 
   /**
    * Retrieve a collection of {@link Edge} objects with a given type and which
@@ -99,10 +98,10 @@ public interface GraphDao {
    *          - 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.
+   * @return - The {@link OperationResult} OperationResult
    * @throws CrudException
    */
-  public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException;
+  public OperationResult getEdges(String type, Map<String, Object> filter) throws CrudException;
 
   /**
    * Insert a new {@link Vertex} into the graph data store.
@@ -111,10 +110,10 @@ public interface GraphDao {
    *          - 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.
+   * @return - The result of the Vertex creation.
    * @throws CrudException
    */
-  public Vertex addVertex(String type, Map<String, Object> properties, String version) throws CrudException;
+  public OperationResult addVertex(String type, Map<String, Object> properties, String version) throws CrudException;
 
   /**
    * Updates an existing {@link Vertex}.
@@ -123,10 +122,10 @@ public interface GraphDao {
    *          - The unique identifier of the vertex to be updated.
    * @param properties
    *          - The properties to associate with the vertex.
-   * @return - The udpated vertex.
+   * @return - The result of the update OperationResult.
    * @throws CrudException
    */
-  public Vertex updateVertex(String id, String type, Map<String, Object> properties, String version) throws CrudException;
+  public OperationResult updateVertex(String id, String type, Map<String, Object> properties, String version) throws CrudException;
 
   /**
    * Removes the specified vertex from the graph data base.
@@ -151,22 +150,20 @@ public interface GraphDao {
    *          - The target vertex for this edge.
    * @param properties
    *          - The properties map to associate with this edge.
-   * @return - The {@link Edge} object that was created.
+   * @return - The {@link OperationResult} OperationResult containing the Edge that was created.
    * @throws CrudException
    */
-  public Edge addEdge(String type, Vertex source, Vertex target, Map<String, Object> properties, String version) throws CrudException;
+  public OperationResult addEdge(String type, Vertex source, Vertex target, Map<String, Object> properties, String version) 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.
+   * @param edge
+   *          - The edge to be updated.
+   * @return - The result of the update OperationResult.
    * @throws CrudException
    */
-  public Edge updateEdge(Edge edge) throws CrudException;
+  public OperationResult updateEdge(Edge edge) throws CrudException;
 
   /**
    * Remove the specified edge from the graph data base.
index 344d797..c8488ba 100644 (file)
  */
 package org.onap.crud.dao.champ;
 
-import net.dongliu.gson.GsonJava8TypeAdapterFactory;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.reflect.TypeToken;
-
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
 import org.apache.http.NameValuePair;
 import org.apache.http.client.utils.URLEncodedUtils;
 import org.apache.http.message.BasicNameValuePair;
 import org.eclipse.jetty.util.security.Password;
-import org.onap.aai.cl.mdc.MdcContext;
-import org.onap.aai.logging.LoggingContext;
 import org.onap.aai.cl.api.Logger;
 import org.onap.aai.cl.eelf.LoggerFactory;
+import org.onap.aai.cl.mdc.MdcContext;
+import org.onap.aai.logging.LoggingContext;
+import org.onap.aai.restclient.client.OperationResult;
+import org.onap.aai.restclient.client.RestClient;
+import org.onap.aai.restclient.enums.RestAuthenticationMode;
 import org.onap.crud.dao.GraphDao;
 import org.onap.crud.entity.Edge;
 import org.onap.crud.entity.Vertex;
 import org.onap.crud.exception.CrudException;
 import org.onap.crud.util.CrudServiceConstants;
-import org.onap.aai.restclient.client.OperationResult;
-import org.onap.aai.restclient.client.RestClient;
-import org.onap.aai.restclient.enums.RestAuthenticationMode;
 import org.slf4j.MDC;
-
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import net.dongliu.gson.GsonJava8TypeAdapterFactory;
 
 public class ChampDao implements GraphDao {
   protected RestClient client;
@@ -75,7 +72,7 @@ public class ChampDao implements GraphDao {
       .registerTypeAdapterFactory(new GsonJava8TypeAdapterFactory())
       .registerTypeAdapter(Vertex.class, new ChampVertexSerializer())
       .registerTypeAdapter(Edge.class, new ChampEdgeSerializer()).create();
-  
+
   public ChampDao() {
   }
 
@@ -112,12 +109,12 @@ public class ChampDao implements GraphDao {
     } else {
       // We didn't find a vertex with the supplied id, so just throw an
       // exception.
-      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No vertex with id " + id + " found in graph");    
+      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No vertex with id " + id + " found in graph");
     }
   }
 
   @Override
-  public Vertex getVertex(String id, String type, String version, Map<String, String> queryParams) throws CrudException {
+  public OperationResult getVertex(String id, String type, String version, Map<String, String> queryParams) throws CrudException {
     StringBuilder strBuild = new StringBuilder(baseObjectUrl);
     strBuild.append("/");
     strBuild.append(id);
@@ -138,11 +135,11 @@ public class ChampDao implements GraphDao {
         throw new CrudException("No vertex with id " + id + "and type " + type + " found in graph",
             javax.ws.rs.core.Response.Status.NOT_FOUND);
       }
-      return vert;
+      return getResult;
     } else {
       // We didn't find a vertex with the supplied id, so just throw an
       // exception.
-        throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No vertex with id " + id + " found in graph");    
+        throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No vertex with id " + id + " found in graph");
     }
   }
 
@@ -158,24 +155,24 @@ public class ChampDao implements GraphDao {
     }
 
     OperationResult getResult = client.get(strBuild.toString(), createHeader(), MediaType.APPLICATION_JSON_TYPE);
-  
+
     if (getResult.getResultCode() == 200) {
       return champGson.fromJson(getResult.getResult(), new TypeToken<List<Edge>>() {
       }.getType());
     } else {
       // We didn't find a vertex with the supplied id, so just throw an
       // exception.
-      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No vertex with id " + id + " found in graph");    
+      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No vertex with id " + id + " found in graph");
     }
   }
 
   @Override
-  public List<Vertex> getVertices(String type, Map<String, Object> filter, String version) throws CrudException {
+  public OperationResult getVertices(String type, Map<String, Object> filter, String version) throws CrudException {
     return getVertices(type, filter, new HashSet<String>(), version);
   }
 
   @Override
-  public List<Vertex> getVertices(String type, Map<String, Object> filter, HashSet<String> properties, String version) throws CrudException {
+  public OperationResult getVertices(String type, Map<String, Object> filter, HashSet<String> properties, String version) throws CrudException {
     filter.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
 
     List<NameValuePair> queryParams = convertToNameValuePair(filter);
@@ -186,16 +183,16 @@ public class ChampDao implements GraphDao {
     OperationResult getResult = client.get(url, createHeader(), MediaType.APPLICATION_JSON_TYPE);
 
     if (getResult.getResultCode() == 200) {
-      return Vertex.collectionFromJson(getResult.getResult(), version);
+      return getResult;
     } else {
       // We didn't find a vertex with the supplied id, so just throw an
       // exception.
-      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No vertices found in graph for given filters");    
+      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No vertices found in graph for given filters");
     }
   }
 
   @Override
-  public Edge getEdge(String id, String type, Map<String, String> queryParams) throws CrudException {
+  public OperationResult getEdge(String id, String type, Map<String, String> queryParams) throws CrudException {
     StringBuilder strBuild = new StringBuilder(baseRelationshipUrl);
     strBuild.append("/");
     strBuild.append(id);
@@ -205,7 +202,7 @@ public class ChampDao implements GraphDao {
         strBuild.append(URLEncodedUtils.format(convertToNameValuePair(queryParams), Charset.defaultCharset()));
     }
     OperationResult getResult = client.get(strBuild.toString(), createHeader(), MediaType.APPLICATION_JSON_TYPE);
-  
+
     if (getResult.getResultCode() == 200) {
       Edge edge = Edge.fromJson(getResult.getResult());
 
@@ -215,33 +212,32 @@ public class ChampDao implements GraphDao {
         throw new CrudException("No edge with id " + id + "and type " + type + " found in graph",
             javax.ws.rs.core.Response.Status.NOT_FOUND);
       }
-      return edge;
+      return getResult;
     } else {
       // We didn't find a edge with the supplied type, so just throw an
       // exception.
-      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No edge with id " + id + " found in graph");    
+      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No edge with id " + id + " found in graph");
     }
   }
 
   @Override
-  public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException {
+  public OperationResult getEdges(String type, Map<String, Object> filter) throws CrudException {
     String url = baseRelationshipUrl + "/filter" + "?"
         + URLEncodedUtils.format(convertToNameValuePair(filter), Charset.defaultCharset());
 
     OperationResult getResult = client.get(url, createHeader(), MediaType.APPLICATION_JSON_TYPE);
 
     if (getResult.getResultCode() == 200) {
-      return champGson.fromJson(getResult.getResult(), new TypeToken<List<Edge>>() {
-      }.getType());
+        return getResult;
     } else {
       // We didn't find a vertex with the supplied id, so just throw an
       // exception.
-      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No edges found in graph  for given filters");    
+      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No edges found in graph  for given filters");
     }
   }
 
   @Override
-  public Vertex addVertex(String type, Map<String, Object> properties, String version) throws CrudException {
+  public OperationResult addVertex(String type, Map<String, Object> properties, String version) throws CrudException {
     String url = baseObjectUrl;
 
     // Add the aai_node_type so that AAI can read the data created by gizmo
@@ -256,7 +252,7 @@ public class ChampDao implements GraphDao {
         MediaType.APPLICATION_JSON_TYPE);
 
     if (getResult.getResultCode() == Response.Status.CREATED.getStatusCode()) {
-      return Vertex.fromJson(getResult.getResult(), version);
+      return getResult;
     } else {
       // We didn't create a vertex with the supplied type, so just throw an
       // exception.
@@ -265,7 +261,7 @@ public class ChampDao implements GraphDao {
   }
 
   @Override
-  public Vertex updateVertex(String id, String type, Map<String, Object> properties, String version) throws CrudException {
+  public OperationResult updateVertex(String id, String type, Map<String, Object> properties, String version) throws CrudException {
     String url = baseObjectUrl + "/" + id;
 
     // Add the aai_node_type so that AAI can read the data created by gizmo
@@ -282,7 +278,7 @@ public class ChampDao implements GraphDao {
         MediaType.APPLICATION_JSON_TYPE);
 
     if (getResult.getResultCode() == Response.Status.OK.getStatusCode()) {
-      return Vertex.fromJson(getResult.getResult(), version);
+      return getResult;
     } else {
       // We didn't create a vertex with the supplied type, so just throw an
       // exception.
@@ -303,12 +299,14 @@ public class ChampDao implements GraphDao {
   }
 
   @Override
-  public Edge addEdge(String type, Vertex source, Vertex target, Map<String, Object> properties, String version) throws CrudException {
+  public OperationResult addEdge(String type, Vertex source, Vertex target, Map<String, Object> properties, String version) throws CrudException {
     String url = baseRelationshipUrl;
 
     // Try requests to ensure source and target exist in Champ
-    Vertex dbSource = getVertex(source.getId().get(), source.getType(), version, new HashMap<String, String>());
-    Vertex dbTarget = getVertex(target.getId().get(), target.getType(), version, new HashMap<String, String>());
+    OperationResult dbSourceOpResult = getVertex(source.getId().get(), source.getType(), version, new HashMap<String, String>());
+    Vertex dbSource = Vertex.fromJson(dbSourceOpResult.getResult(), version);
+    OperationResult dbTargetOpResult = getVertex(target.getId().get(), target.getType(), version, new HashMap<String, String>());
+    Vertex dbTarget = Vertex.fromJson(dbTargetOpResult.getResult(), version);
 
     Edge.Builder insertEdgeBuilder = new Edge.Builder(type).source(dbSource).target(dbTarget);
     properties.forEach(insertEdgeBuilder::property);
@@ -319,7 +317,7 @@ public class ChampDao implements GraphDao {
         MediaType.APPLICATION_JSON_TYPE);
 
     if (getResult.getResultCode() == Response.Status.CREATED.getStatusCode()) {
-      return Edge.fromJson(getResult.getResult());
+      return getResult;
     } else {
       // We didn't create an edge with the supplied type, so just throw an
       // exception.
@@ -328,7 +326,7 @@ public class ChampDao implements GraphDao {
   }
 
   @Override
-  public Edge updateEdge(Edge edge) throws CrudException {
+  public OperationResult updateEdge(Edge edge) throws CrudException {
     if (!edge.getId().isPresent()) {
       throw new CrudException("Unable to identify edge: " + edge.toString(), Response.Status.BAD_REQUEST);
     }
@@ -339,7 +337,7 @@ public class ChampDao implements GraphDao {
         MediaType.APPLICATION_JSON_TYPE);
 
     if (getResult.getResultCode() == Response.Status.OK.getStatusCode()) {
-      return Edge.fromJson(getResult.getResult());
+      return getResult;
     } else {
       // We didn't create an edge with the supplied type, so just throw an
       // exception.
@@ -355,7 +353,7 @@ public class ChampDao implements GraphDao {
     if (getResult.getResultCode() != 200) {
       // We didn't find an edge with the supplied type, so just throw an
       // exception.
-      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No edge with id " + id + " found in graph");    
+      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No edge with id " + id + " found in graph");
     }
   }
 
@@ -524,7 +522,7 @@ public class ChampDao implements GraphDao {
     if (getResult.getResultCode() != 200) {
       // We didn't find an edge with the supplied type, so just throw an
       // exception.
-      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No edge with id " + id + " found in graph");    
+      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No edge with id " + id + " found in graph");
     }
   }
 
@@ -546,7 +544,7 @@ public class ChampDao implements GraphDao {
     } else {
       // We didn't find an edge with the supplied id, so just throw an
       // exception.
-      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No edge with id " + id + " found in graph");    
+      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No edge with id " + id + " found in graph");
     }
   }
 
@@ -567,7 +565,7 @@ public class ChampDao implements GraphDao {
     } else {
       // We didn't find a vertex with the supplied id, so just throw an
       // exception.
-      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No vertex with id " + id + " found in graph");    
+      throw createErrorException(getResult, javax.ws.rs.core.Response.Status.NOT_FOUND, "No vertex with id " + id + " found in graph");
     }
   }
 
@@ -588,15 +586,15 @@ public class ChampDao implements GraphDao {
 
     return nvpList;
   }
-  
+
   private Map<String, List<String>> createHeader() {
     Map<String, List<String>> headers = new HashMap<>();
     headers.put(HEADER_FROM_APP, Arrays.asList(FROM_APP_NAME));
     headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(MdcContext.MDC_REQUEST_ID)));
     return headers;
   }
-  
-  private CrudException createErrorException(OperationResult result, javax.ws.rs.core.Response.Status defaultErrorCode , String defaultErrorMsg) 
+
+  private CrudException createErrorException(OperationResult result, javax.ws.rs.core.Response.Status defaultErrorCode , String defaultErrorMsg)
   {
       CrudException ce = null;
       if(result != null)
index 63b84fd..958c227 100644 (file)
  */
 package org.onap.crud.event;
 
+import java.util.Objects;
+import javax.ws.rs.core.Response.Status;
+import org.onap.crud.exception.CrudException;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.annotations.SerializedName;
 
-import org.onap.crud.exception.CrudException;
-
-import javax.ws.rs.core.Response.Status;
-
 public class GraphEvent {
 
   public enum GraphEventOperation {
@@ -171,9 +170,14 @@ public class GraphEvent {
 
   @Override
   public String toString() {
-
     return toJson();
   }
+  
+  @Override
+  public int hashCode() {
+    return Objects.hash(this.dbTransactionId, this.timestamp, this.edge, this.vertex, this.operation,
+              this.result);
+  }
 
   public String getObjectKey() {
     if (this.getVertex() != null) {
index 4f914cf..81613dd 100644 (file)
@@ -173,7 +173,7 @@ public class GraphEventHeader {
                   .append(requestId, rhs.requestId)
                   .append(timestamp, rhs.timestamp)
                   .append(sourceName, rhs.sourceName)
-                  .append(eventType, rhs.sourceName)
+                  .append(eventType, rhs.eventType)
                   .append(validationEntityType, rhs.validationEntityType)
                   .append(validationTopEntityType, rhs.validationTopEntityType)
                   .append(entityLink, rhs.entityLink)
index 0c66d81..0a81884 100644 (file)
@@ -24,10 +24,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-
-import javax.ws.rs.core.Response.Status;
-
 import org.onap.crud.entity.Edge;
 import org.onap.crud.entity.Vertex;
 import org.onap.crud.exception.CrudException;
index c9a5805..afabe7e 100644 (file)
@@ -26,7 +26,6 @@ import java.util.HashSet;
 import java.util.Map;\r
 import java.util.Map.Entry;\r
 import java.util.Set;\r
-\r
 import javax.security.auth.x500.X500Principal;\r
 import javax.servlet.http.HttpServletRequest;\r
 import javax.ws.rs.Consumes;\r
@@ -37,20 +36,21 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;\r
 import javax.ws.rs.Produces;\r
 import javax.ws.rs.core.Context;\r
+import javax.ws.rs.core.EntityTag;\r
 import javax.ws.rs.core.HttpHeaders;\r
 import javax.ws.rs.core.MediaType;\r
 import javax.ws.rs.core.Response;\r
-import javax.ws.rs.core.UriInfo;\r
 import javax.ws.rs.core.Response.Status;\r
-\r
+import javax.ws.rs.core.UriInfo;\r
+import org.apache.commons.lang3.tuple.ImmutablePair;\r
+import org.onap.aai.cl.api.Logger;\r
+import org.onap.aai.cl.eelf.LoggerFactory;\r
 import org.onap.aai.exceptions.AAIException;\r
 import org.onap.aai.serialization.db.EdgeProperty;\r
 import org.onap.aai.serialization.db.EdgeRule;\r
 import org.onap.aai.serialization.db.EdgeRules;\r
 import org.onap.aai.serialization.db.EdgeType;\r
 import org.onap.aaiauth.auth.Auth;\r
-import org.onap.aai.cl.api.Logger;\r
-import org.onap.aai.cl.eelf.LoggerFactory;\r
 import org.onap.crud.exception.CrudException;\r
 import org.onap.crud.logging.CrudServiceMsgs;\r
 import org.onap.crud.logging.LoggingUtil;\r
@@ -59,7 +59,6 @@ import org.onap.crud.util.CrudServiceConstants;
 import org.onap.schema.EdgeRulesLoader;\r
 import org.onap.schema.RelationshipSchemaValidator;\r
 import org.slf4j.MDC;\r
-\r
 import com.google.gson.Gson;\r
 import com.google.gson.JsonElement;\r
 import com.google.gson.JsonPrimitive;\r
@@ -74,78 +73,78 @@ public class AaiResourceService {
 \r
   private String mediaType = MediaType.APPLICATION_JSON;\r
   public static final String HTTP_PATCH_METHOD_OVERRIDE = "X-HTTP-Method-Override";\r
-  \r
+\r
   private Auth auth;\r
   AbstractGraphDataService graphDataService;\r
   Gson gson = new Gson();\r
-  \r
+\r
   private Logger logger      = LoggerFactory.getInstance().getLogger(AaiResourceService.class.getName());\r
   private Logger auditLogger = LoggerFactory.getInstance().getAuditLogger(AaiResourceService.class.getName());\r
\r
+\r
   public AaiResourceService() {}\r
-  \r
+\r
   /**\r
    * Creates a new instance of the AaiResourceService.\r
-   * \r
+   *\r
    * @param crudGraphDataService - Service used for interacting with the graph.\r
-   * \r
+   *\r
    * @throws Exception\r
    */\r
   public AaiResourceService(AbstractGraphDataService graphDataService) throws Exception {\r
     this.graphDataService = graphDataService;\r
     this.auth = new Auth(CrudServiceConstants.CRD_AUTH_FILE);\r
   }\r
-  \r
+\r
   /**\r
    * Perform any one-time initialization required when starting the service.\r
    */\r
   public void startup() {\r
-    \r
+\r
     if(logger.isDebugEnabled()) {\r
       logger.debug("AaiResourceService started!");\r
     }\r
   }\r
-  \r
-  \r
+\r
+\r
   /**\r
    * Creates a new relationship in the graph, automatically populating the edge\r
    * properties based on the A&AI edge rules.\r
-   * \r
+   *\r
    * @param content - Json structure describing the relationship to create.\r
    * @param type    - Relationship type supplied as a URI parameter.\r
    * @param uri     - Http request uri\r
    * @param headers - Http request headers\r
    * @param uriInfo - Http URI info field\r
    * @param req     - Http request structure.\r
-   * \r
+   *\r
    * @return - Standard HTTP response.\r
    */\r
   @POST\r
   @Path("/relationships/{type}/")\r
   @Consumes({MediaType.APPLICATION_JSON})\r
   @Produces({MediaType.APPLICATION_JSON})\r
-  public Response createRelationship(String content, \r
-                                     @PathParam("type") String type, \r
+  public Response createRelationship(String content,\r
+                                     @PathParam("type") String type,\r
                                      @PathParam("uri") @Encoded String uri,\r
-                                     @Context HttpHeaders headers, \r
+                                     @Context HttpHeaders headers,\r
                                      @Context UriInfo uriInfo,\r
                                      @Context HttpServletRequest req) {\r
-    \r
+\r
     LoggingUtil.initMdcContext(req, headers);\r
 \r
     if(logger.isDebugEnabled()) {\r
       logger.debug("Incoming request..." + content);\r
     }\r
-    \r
+\r
     Response response = null;\r
 \r
     if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {\r
-      \r
+\r
       try {\r
-        \r
+\r
         // Extract the edge payload from the request.\r
-        EdgePayload payload = EdgePayload.fromJson(content);   \r
-        \r
+        EdgePayload payload = EdgePayload.fromJson(content);\r
+\r
         // Do some basic validation on the payload.\r
         if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {\r
           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);\r
@@ -156,50 +155,50 @@ public class AaiResourceService {
         if (payload.getType() != null && !payload.getType().equals(type)) {\r
           throw new CrudException("Edge Type mismatch", Status.BAD_REQUEST);\r
         }\r
-        \r
+\r
         // Apply the edge rules to our edge.\r
         payload = applyEdgeRulesToPayload(payload);\r
-        \r
+\r
         if(logger.isDebugEnabled()) {\r
           logger.debug("Creating AAI edge using version " + EdgeRulesLoader.getLatestSchemaVersion() );\r
         }\r
-        \r
+\r
         // Now, create our edge in the graph store.\r
-        String result = graphDataService.addEdge(EdgeRulesLoader.getLatestSchemaVersion(), type, payload);\r
-        response = Response.status(Status.CREATED).entity(result).type(mediaType).build();\r
-        \r
+        ImmutablePair<EntityTag, String> result = graphDataService.addEdge(EdgeRulesLoader.getLatestSchemaVersion(), type, payload);\r
+        response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();\r
+\r
       } catch (CrudException e) {\r
 \r
         response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();\r
-      }   \r
+      }\r
     }\r
-    \r
+\r
     LoggingUtil.logRestRequest(logger, auditLogger, req, response);\r
     return response;\r
   }\r
-  \r
-  \r
+\r
+\r
   /**\r
    * Creates a new relationship in the graph, automatically populating the edge\r
    * properties based on the A&AI edge rules.\r
-   * \r
+   *\r
    * @param content - Json structure describing the relationship to create.\r
    * @param uri     - Http request uri\r
    * @param headers - Http request headers\r
    * @param uriInfo - Http URI info field\r
    * @param req     - Http request structure.\r
-   * \r
+   *\r
    * @return - Standard HTTP response.\r
-   *    \r
+   *\r
    */\r
   @POST\r
   @Path("/relationships/")\r
   @Consumes({MediaType.APPLICATION_JSON})\r
   @Produces({MediaType.APPLICATION_JSON})\r
-  public Response createRelationship(String content, \r
-                                     @PathParam("uri") @Encoded String uri, \r
+  public Response createRelationship(String content,\r
+                                     @PathParam("uri") @Encoded String uri,\r
                                      @Context HttpHeaders headers,\r
-                                     @Context UriInfo uriInfo, \r
+                                     @Context UriInfo uriInfo,\r
                                      @Context HttpServletRequest req) {\r
 \r
     LoggingUtil.initMdcContext(req, headers);\r
@@ -210,10 +209,10 @@ public class AaiResourceService {
     if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {\r
 \r
       try {\r
-        \r
+\r
         // Extract the edge payload from the request.\r
         EdgePayload payload = EdgePayload.fromJson(content);\r
-        \r
+\r
         // Do some basic validation on the payload.\r
         if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {\r
           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);\r
@@ -224,14 +223,14 @@ public class AaiResourceService {
         if (payload.getType() == null || payload.getType().isEmpty()) {\r
           throw new CrudException("Missing Edge Type ", Status.BAD_REQUEST);\r
         }\r
-        \r
+\r
         // Apply the edge rules to our edge.\r
         payload = applyEdgeRulesToPayload(payload);\r
-        \r
+\r
         // Now, create our edge in the graph store.\r
-        String result = graphDataService.addEdge(EdgeRulesLoader.getLatestSchemaVersion(), payload.getType(), payload);\r
-        response = Response.status(Status.CREATED).entity(result).type(mediaType).build();\r
-      \r
+        ImmutablePair<EntityTag, String> result = graphDataService.addEdge(EdgeRulesLoader.getLatestSchemaVersion(), payload.getType(), payload);\r
+        response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();\r
+\r
       } catch (CrudException ce) {\r
         response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();\r
       } catch (Exception e) {\r
@@ -246,17 +245,17 @@ public class AaiResourceService {
     return response;\r
   }\r
 \r
-  \r
-  \r
+\r
+\r
   /**\r
    * Upserts a relationship into the graph, automatically populating the edge properties\r
    * based on the A&AI edge rules.  The behaviour is as follows:\r
    * <p>\r
-   * <li>If no relationship with the supplied identifier already exists, then a new relationship \r
+   * <li>If no relationship with the supplied identifier already exists, then a new relationship\r
    * is created with that id.<br>\r
-   * <li>If a relationship with the supplied id DOES exist, then it is replaced with the supplied \r
+   * <li>If a relationship with the supplied id DOES exist, then it is replaced with the supplied\r
    * content.\r
-   * \r
+   *\r
    * @param content - Json structure describing the relationship to create.\r
    * @param type    - Relationship type supplied as a URI parameter.\r
    * @param id      - Edge identifier.\r
@@ -264,19 +263,19 @@ public class AaiResourceService {
    * @param headers - Http request headers\r
    * @param uriInfo - Http URI info field\r
    * @param req     - Http request structure.\r
-   * \r
+   *\r
    * @return - Standard HTTP response.\r
    */\r
   @PUT\r
   @Path("/relationships/{type}/{id}")\r
   @Consumes({MediaType.APPLICATION_JSON})\r
   @Produces({MediaType.APPLICATION_JSON})\r
-  public Response upsertEdge(String content, \r
-                             @PathParam("type") String type, \r
+  public Response upsertEdge(String content,\r
+                             @PathParam("type") String type,\r
                              @PathParam("id") String id,\r
-                             @PathParam("uri") @Encoded String uri, \r
+                             @PathParam("uri") @Encoded String uri,\r
                              @Context HttpHeaders headers,\r
-                             @Context UriInfo uriInfo, \r
+                             @Context UriInfo uriInfo,\r
                              @Context HttpServletRequest req) {\r
     LoggingUtil.initMdcContext(req, headers);\r
 \r
@@ -284,12 +283,12 @@ public class AaiResourceService {
     Response response = null;\r
 \r
     if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) {\r
-      \r
+\r
       try {\r
-        \r
+\r
         // Extract the edge payload from the request.\r
         EdgePayload payload = EdgePayload.fromJson(content);\r
-        \r
+\r
         // Do some basic validation on the payload.\r
         if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {\r
           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);\r
@@ -297,47 +296,45 @@ public class AaiResourceService {
         if (payload.getId() != null && !payload.getId().equals(id)) {\r
           throw new CrudException("ID Mismatch", Status.BAD_REQUEST);\r
         }\r
-        \r
+\r
         // Apply the edge rules to our edge.\r
         payload = applyEdgeRulesToPayload(payload);\r
-        \r
-        String result;\r
+        ImmutablePair<EntityTag, String> result;\r
         if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null &&\r
             headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE).equalsIgnoreCase("PATCH")) {\r
           result = graphDataService.patchEdge(EdgeRulesLoader.getLatestSchemaVersion(), id, type, payload);\r
+          response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();\r
         } else {\r
-\r
           result = graphDataService.updateEdge(EdgeRulesLoader.getLatestSchemaVersion(), id, type, payload);\r
+          response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();\r
         }\r
 \r
-        response = Response.status(Status.OK).entity(result).type(mediaType).build();\r
-        \r
       } catch (CrudException ce) {\r
         response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();\r
       } catch (Exception e) {\r
         response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();\r
       }\r
-      \r
+\r
     } else {\r
-      \r
+\r
       response = Response.status(Status.FORBIDDEN).entity(content)\r
           .type(MediaType.APPLICATION_JSON).build();\r
     }\r
-    \r
+\r
     LoggingUtil.logRestRequest(logger, auditLogger, req, response);\r
     return response;\r
   }\r
-  \r
-  \r
+\r
+\r
   /**\r
-   * Retrieves the properties defined in the edge rules for a relationship between the \r
+   * Retrieves the properties defined in the edge rules for a relationship between the\r
    * supplied vertex types.\r
-   * \r
+   *\r
    * @param sourceVertexType - Type of source vertex for the relationship.\r
    * @param targetVertexType - Type of target vertex for the relationship.\r
-   * \r
+   *\r
    * @return - The defined properties for the relationship type.\r
-   *  \r
+   *\r
    * @throws CrudException\r
    */\r
   private Map<EdgeProperty, String> getEdgeRuleProperties(String sourceVertexType, String targetVertexType) throws CrudException {\r
@@ -345,119 +342,119 @@ public class AaiResourceService {
     if(logger.isDebugEnabled()) {\r
       logger.debug("Lookup db edge rules for " + sourceVertexType + " -> " + targetVertexType);\r
     }\r
-    \r
+\r
     EdgeRules rules = EdgeRules.getInstance();\r
     EdgeRule rule;\r
     try {\r
-      \r
+\r
       if(logger.isDebugEnabled()) {\r
         logger.debug("Lookup by edge type TREE");\r
       }\r
-      \r
+\r
       // We have no way of knowing in advance whether our relationship is considered to\r
       // be a tree or cousing relationship, so try looking it up as a tree type first.\r
       rule = rules.getEdgeRule(EdgeType.TREE, sourceVertexType, targetVertexType);\r
-      \r
+\r
     } catch (AAIException e) {\r
       try {\r
-        \r
+\r
         if(logger.isDebugEnabled()) {\r
           logger.debug("Lookup by edge type COUSIN");\r
         }\r
-        \r
+\r
         // If we are here, then our lookup by 'tree' type failed, so try looking it up\r
         // as a 'cousin' relationship.\r
         rule = rules.getEdgeRule(EdgeType.COUSIN, sourceVertexType, targetVertexType);\r
-        \r
+\r
       } catch (AAIException e1) {\r
-        \r
+\r
         // If we're here then we failed to find edge rules for this relationship.  Time to\r
         // give up...\r
         throw new CrudException("No edge rules for " + sourceVertexType + " -> " + targetVertexType, Status.NOT_FOUND);\r
       }\r
     } catch (Exception e) {\r
-      \r
-      throw new CrudException("General failure getting edge rule properties - " + \r
+\r
+      throw new CrudException("General failure getting edge rule properties - " +\r
                               e.getMessage(), Status.INTERNAL_SERVER_ERROR);\r
     }\r
-    \r
+\r
     return rule.getEdgeProperties();\r
   }\r
-  \r
-  \r
+\r
+\r
   /**\r
    * This method takes an inbound edge request payload, looks up the edge rules for the\r
    * sort of relationship defined in the payload, and automatically applies the defined\r
    * edge properties to it.\r
-   * \r
+   *\r
    * @param payload - The original edge request payload\r
-   * \r
+   *\r
    * @return - An updated edge request payload, with the properties defined in the edge\r
    *           rules automatically populated.\r
-   *           \r
+   *\r
    * @throws CrudException\r
    */\r
   public EdgePayload applyEdgeRulesToPayload(EdgePayload payload) throws CrudException {\r
-    \r
+\r
     // Extract the types for both the source and target vertices.\r
     String srcType = RelationshipSchemaValidator.vertexTypeFromUri(payload.getSource());\r
     String tgtType = RelationshipSchemaValidator.vertexTypeFromUri(payload.getTarget());\r
 \r
       // Now, get the default properties for this edge based on the edge rules definition...\r
       Map<EdgeProperty, String> props = getEdgeRuleProperties(srcType, tgtType);\r
-      \r
+\r
       // ...and merge them with any custom properties provided in the request.\r
       JsonElement mergedProperties = mergeProperties(payload.getProperties(), props);\r
       payload.setProperties(mergedProperties);\r
-    \r
-    \r
+\r
+\r
     if(logger.isDebugEnabled()) {\r
       logger.debug("Edge properties after applying rules for '" + srcType + " -> " + tgtType + "': " + mergedProperties);\r
     }\r
-    \r
+\r
     return payload;\r
   }\r
-  \r
-  \r
+\r
+\r
   /**\r
    * Given a set of edge properties extracted from an edge request payload and a set of properties\r
    * taken from the db edge rules, this method merges them into one set of properties.\r
    * <p>\r
    * If the client has attempted to override the defined value for a property in the db edge rules\r
    * then the request will be rejected as invalid.\r
-   * \r
+   *\r
    * @param propertiesFromRequest - Set of properties from the edge request.\r
    * @param propertyDefaults      - Set of properties from the db edge rules.\r
-   * \r
+   *\r
    * @return - A merged set of properties.\r
-   * \r
+   *\r
    * @throws CrudException\r
    */\r
   public JsonElement mergeProperties(JsonElement propertiesFromRequest, Map<EdgeProperty, String> propertyDefaults) throws CrudException {\r
-        \r
+\r
     // Convert the properties from the edge payload into something we can\r
     // manipulate.\r
     Set<Map.Entry<String, JsonElement>> properties = new HashSet<Map.Entry<String, JsonElement>>();\r
     properties.addAll(propertiesFromRequest.getAsJsonObject().entrySet());\r
-    \r
+\r
     Set<String> propertyKeys = new HashSet<String>();\r
     for(Map.Entry<String, JsonElement> property : properties) {\r
       propertyKeys.add(property.getKey());\r
     }\r
-    \r
+\r
     // Now, merge in the properties specified in the Db Edge Rules.\r
     for(EdgeProperty defProperty : propertyDefaults.keySet()) {\r
-      \r
+\r
       // If the edge rules property was explicitly specified by the\r
       // client then we will reject the request...\r
       if(!propertyKeys.contains(defProperty.toString())) {\r
         properties.add(new AbstractMap.SimpleEntry<String, JsonElement>(defProperty.toString(),\r
-            (JsonElement)(new JsonPrimitive(propertyDefaults.get(defProperty)))));\r
-        \r
+            (new JsonPrimitive(propertyDefaults.get(defProperty)))));\r
+\r
       } else {\r
-        throw new CrudException("Property " + defProperty + " defined in db edge rules can not be overriden by the client.", \r
+        throw new CrudException("Property " + defProperty + " defined in db edge rules can not be overriden by the client.",\r
                                 Status.BAD_REQUEST);\r
-      }    \r
+      }\r
     }\r
 \r
     Object[] propArray = properties.toArray();\r
@@ -465,7 +462,7 @@ public class AaiResourceService {
     sb.append("{");\r
     boolean first=true;\r
     for(int i=0; i<propArray.length; i++) {\r
-      \r
+\r
       Map.Entry<String, JsonElement> entry = (Entry<String, JsonElement>) propArray[i];\r
       if(!first) {\r
         sb.append(",");\r
@@ -474,7 +471,7 @@ public class AaiResourceService {
       first=false;\r
     }\r
     sb.append("}");\r
-    \r
+\r
     // We're done.  Return the result as a JsonElement.\r
     return gson.fromJson(sb.toString(), JsonElement.class);\r
   }\r
@@ -482,45 +479,45 @@ public class AaiResourceService {
 \r
   /**\r
    * Invokes authentication validation on an incoming HTTP request.\r
-   * \r
+   *\r
    * @param req                    - The HTTP request.\r
    * @param uri                    - HTTP URI\r
    * @param content                - Payload of the HTTP request.\r
    * @param action                 - What HTTP action is being performed (GET/PUT/POST/PATCH/DELETE)\r
    * @param authPolicyFunctionName - Policy function being invoked.\r
-   * \r
+   *\r
    * @return true  - if the request passes validation,\r
    *         false - otherwise.\r
    */\r
-  protected boolean validateRequest(HttpServletRequest req, \r
-                                    String uri, \r
+  protected boolean validateRequest(HttpServletRequest req,\r
+                                    String uri,\r
                                     String content,\r
-                                    Action action, \r
+                                    Action action,\r
                                     String authPolicyFunctionName) {\r
     try {\r
       String cipherSuite = (String) req.getAttribute("javax.servlet.request.cipher_suite");\r
       String authUser = null;\r
       if (cipherSuite != null) {\r
-                \r
+\r
         X509Certificate[] certChain = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");\r
         X509Certificate clientCert = certChain[0];\r
         X500Principal subjectDn = clientCert.getSubjectX500Principal();\r
         authUser = subjectDn.toString();\r
       }\r
-      \r
+\r
       return this.auth.validateRequest(authUser.toLowerCase(), action.toString() + ":" + authPolicyFunctionName);\r
-      \r
+\r
     } catch (Exception e) {\r
       logResult(action, uri, e);\r
       return false;\r
     }\r
   }\r
-  \r
+\r
   protected void logResult(Action op, String uri, Exception e) {\r
 \r
-    logger.error(CrudServiceMsgs.EXCEPTION_DURING_METHOD_CALL, \r
-                 op.toString(), \r
-                 uri, \r
+    logger.error(CrudServiceMsgs.EXCEPTION_DURING_METHOD_CALL,\r
+                 op.toString(),\r
+                 uri,\r
                  e.getStackTrace().toString());\r
 \r
     // Clear the MDC context so that no other transaction inadvertently\r
index 9c7e0d4..7c1168e 100644 (file)
@@ -25,11 +25,14 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-
+import javax.ws.rs.core.EntityTag;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response.Status;
-
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.onap.aai.restclient.client.OperationResult;
 import org.onap.crud.dao.GraphDao;
+import org.onap.crud.dao.champ.ChampEdgeSerializer;
+import org.onap.crud.dao.champ.ChampVertexSerializer;
 import org.onap.crud.entity.Edge;
 import org.onap.crud.entity.Vertex;
 import org.onap.crud.exception.CrudException;
@@ -37,50 +40,65 @@ import org.onap.crud.parser.CrudResponseBuilder;
 import org.onap.crud.util.CrudServiceUtil;
 import org.onap.schema.OxmModelValidator;
 import org.onap.schema.RelationshipSchemaValidator;
-
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
 import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+import net.dongliu.gson.GsonJava8TypeAdapterFactory;
 
 public abstract class AbstractGraphDataService {
   protected GraphDao daoForGet;
   protected GraphDao dao;
-  
+
   public AbstractGraphDataService() throws CrudException {
     CrudServiceUtil.loadModels();
   }
 
-  public String getEdge(String version, String id, String type, Map<String, String> queryParams) throws CrudException {
+  public ImmutablePair<EntityTag, String> getEdge(String version, String id, String type, Map<String, String> queryParams) throws CrudException {
     RelationshipSchemaValidator.validateType(version, type);
-    Edge edge = daoForGet.getEdge(id, type, queryParams);
-
-    return CrudResponseBuilder.buildGetEdgeResponse(RelationshipSchemaValidator.validateOutgoingPayload(version, edge), version);
+    OperationResult operationResult = daoForGet.getEdge(id, type, queryParams);
+    EntityTag entityTag = CrudServiceUtil.getETagFromHeader(operationResult.getHeaders());
+    Edge edge = Edge.fromJson(operationResult.getResult());
+    return new ImmutablePair<>(entityTag, CrudResponseBuilder.buildGetEdgeResponse(RelationshipSchemaValidator.validateOutgoingPayload(version, edge), version));
   }
-  
-  public String getEdges(String version, String type, Map<String, String> filter) throws CrudException {
+
+  public ImmutablePair<EntityTag, String> getEdges(String version, String type, Map<String, String> filter) throws CrudException {
+     Gson champGson = new GsonBuilder()
+              .registerTypeAdapterFactory(new GsonJava8TypeAdapterFactory())
+              .registerTypeAdapter(Vertex.class, new ChampVertexSerializer())
+              .registerTypeAdapter(Edge.class, new ChampEdgeSerializer()).create();
     RelationshipSchemaValidator.validateType(version, type);
-    List<Edge> items = daoForGet.getEdges(type, RelationshipSchemaValidator.resolveCollectionfilter(version, type, filter));
-    return CrudResponseBuilder.buildGetEdgesResponse(items, version);
+    OperationResult operationResult = daoForGet.getEdges(type, RelationshipSchemaValidator.resolveCollectionfilter(version, type, filter));
+    List<Edge> items = champGson.fromJson(operationResult.getResult(), new TypeToken<List<Edge>>() {
+    }.getType());
+    EntityTag entityTag = CrudServiceUtil.getETagFromHeader(operationResult.getHeaders());
+    return new ImmutablePair<>(entityTag, CrudResponseBuilder.buildGetEdgesResponse(items, version));
   }
-  
-  public String getVertex(String version, String id, String type, Map<String, String> queryParams) throws CrudException {
+
+  public ImmutablePair<EntityTag, String> getVertex(String version, String id, String type, Map<String, String> queryParams) throws CrudException {
     type = OxmModelValidator.resolveCollectionType(version, type);
-    Vertex vertex = daoForGet.getVertex(id, type, version, queryParams);
+    OperationResult vertexOpResult = daoForGet.getVertex(id, type, version, queryParams);
+    Vertex vertex = Vertex.fromJson(vertexOpResult.getResult(), version);
     List<Edge> edges = daoForGet.getVertexEdges(id, queryParams);
-    return CrudResponseBuilder.buildGetVertexResponse(OxmModelValidator.validateOutgoingPayload(version, vertex), edges,
-        version);
+    EntityTag entityTag = CrudServiceUtil.getETagFromHeader(vertexOpResult.getHeaders());
+    return new ImmutablePair<>(entityTag, CrudResponseBuilder.buildGetVertexResponse(OxmModelValidator.validateOutgoingPayload(version, vertex), edges,
+        version));
   }
 
-  public String getVertices(String version, String type, Map<String, String> filter, HashSet<String> properties) throws CrudException {
+  public ImmutablePair<EntityTag, String> getVertices(String version, String type, Map<String, String> filter, HashSet<String> properties) throws CrudException {
     type = OxmModelValidator.resolveCollectionType(version, type);
-    List<Vertex> items = daoForGet.getVertices(type, OxmModelValidator.resolveCollectionfilter(version, type, filter), properties, version);
-    return CrudResponseBuilder.buildGetVerticesResponse(items, version);
+    OperationResult operationResult = daoForGet.getVertices(type, OxmModelValidator.resolveCollectionfilter(version, type, filter), properties, version);
+    List<Vertex> vertices = Vertex.collectionFromJson(operationResult.getResult(), version);
+    EntityTag entityTag = CrudServiceUtil.getETagFromHeader(operationResult.getHeaders());
+    return new ImmutablePair<>(entityTag, CrudResponseBuilder.buildGetVerticesResponse(vertices, version));
   }
-  
+
   public String addBulk(String version, BulkPayload payload, HttpHeaders headers) throws CrudException {
-    HashMap<String, Vertex> vertices = new HashMap<String, Vertex>();
-    HashMap<String, Edge> edges = new HashMap<String, Edge>();
-    
-    String txId = dao.openTransaction();   
-     
+    HashMap<String, Vertex> vertices = new HashMap<>();
+    HashMap<String, Edge> edges = new HashMap<>();
+
+    String txId = dao.openTransaction();
+
     try {
       // Step 1. Handle edge deletes (must happen before vertex deletes)
       for (JsonElement v : payload.getRelationships()) {
@@ -98,8 +116,8 @@ public abstract class AbstractGraphDataService {
           RelationshipSchemaValidator.validateType(version, edgePayload.getType());
           deleteBulkEdge(edgePayload.getId(), version, edgePayload.getType(), txId);
         }
-      } 
-      
+      }
+
       // Step 2: Handle vertex deletes
       for (JsonElement v : payload.getObjects()) {
         List<Map.Entry<String, JsonElement>> entries = new ArrayList<Map.Entry<String, JsonElement>>(
@@ -118,7 +136,7 @@ public abstract class AbstractGraphDataService {
           deleteBulkVertex(vertexPayload.getId(), version, type, txId);
         }
       }
-      
+
       // Step 3: Handle vertex add/modify (must happen before edge adds)
       for (JsonElement v : payload.getObjects()) {
         List<Map.Entry<String, JsonElement>> entries = new ArrayList<Map.Entry<String, JsonElement>>(
@@ -130,21 +148,21 @@ public abstract class AbstractGraphDataService {
         Map.Entry<String, JsonElement> opr = entries.get(0);
         Map.Entry<String, JsonElement> item = entries.get(1);
         VertexPayload vertexPayload = VertexPayload.fromJson(item.getValue().getAsJsonObject().toString());
-        
+
         // Add vertex
         if (opr.getValue().getAsString().equalsIgnoreCase("add")) {
-          vertexPayload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(vertexPayload.getProperties(), 
-              headers, true));  
+          vertexPayload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(vertexPayload.getProperties(),
+              headers, true));
           Vertex validatedVertex = OxmModelValidator.validateIncomingUpsertPayload(null, version, vertexPayload.getType(),
               vertexPayload.getProperties());
           Vertex persistedVertex = addBulkVertex(validatedVertex, version, txId);
           Vertex outgoingVertex = OxmModelValidator.validateOutgoingPayload(version, persistedVertex);
           vertices.put(item.getKey(), outgoingVertex);
         }
-        
-        // Update vertex 
+
+        // Update vertex
         else if (opr.getValue().getAsString().equalsIgnoreCase("modify")) {
-          vertexPayload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(vertexPayload.getProperties(), 
+          vertexPayload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(vertexPayload.getProperties(),
               headers, false));
           Vertex validatedVertex = OxmModelValidator.validateIncomingUpsertPayload(vertexPayload.getId(), version,
               vertexPayload.getType(), vertexPayload.getProperties());
@@ -152,18 +170,19 @@ public abstract class AbstractGraphDataService {
           Vertex outgoingVertex = OxmModelValidator.validateOutgoingPayload(version, persistedVertex);
           vertices.put(item.getKey(), outgoingVertex);
         }
-        
-        // Patch vertex 
+
+        // Patch vertex
         else if (opr.getValue().getAsString().equalsIgnoreCase("patch")) {
           if ( (vertexPayload.getId() == null) || (vertexPayload.getType() == null) ) {
             throw new CrudException("id and type must be specified for patch request", Status.BAD_REQUEST);
           }
-          
-          vertexPayload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(vertexPayload.getProperties(), 
+
+          vertexPayload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(vertexPayload.getProperties(),
               headers, false));
-          
-          Vertex existingVertex = dao.getVertex(vertexPayload.getId(), OxmModelValidator.resolveCollectionType(version, vertexPayload.getType()), version, new HashMap<String, String>());
-          Vertex validatedVertex = OxmModelValidator.validateIncomingPatchPayload(vertexPayload.getId(), 
+
+          OperationResult existingVertexOpResult = dao.getVertex(vertexPayload.getId(), OxmModelValidator.resolveCollectionType(version, vertexPayload.getType()), version, new HashMap<String, String>());
+          Vertex existingVertex = Vertex.fromJson(existingVertexOpResult.getResult(), version);
+          Vertex validatedVertex = OxmModelValidator.validateIncomingPatchPayload(vertexPayload.getId(),
               version, vertexPayload.getType(), vertexPayload.getProperties(), existingVertex);
           Vertex persistedVertex = updateBulkVertex(validatedVertex, vertexPayload.getId(), version, txId);
           Vertex outgoingVertex = OxmModelValidator.validateOutgoingPayload(version, persistedVertex);
@@ -171,7 +190,7 @@ public abstract class AbstractGraphDataService {
         }
       }
 
-      // Step 4: Handle edge add/modify 
+      // Step 4: Handle edge add/modify
       for (JsonElement v : payload.getRelationships()) {
         List<Map.Entry<String, JsonElement>> entries = new ArrayList<Map.Entry<String, JsonElement>>(
             v.getAsJsonObject().entrySet());
@@ -185,7 +204,7 @@ public abstract class AbstractGraphDataService {
 
         // Add/Update edge
         if (opr.getValue().getAsString().equalsIgnoreCase("add")
-            || opr.getValue().getAsString().equalsIgnoreCase("modify") 
+            || opr.getValue().getAsString().equalsIgnoreCase("modify")
             || opr.getValue().getAsString().equalsIgnoreCase("patch")) {
           Edge validatedEdge;
           Edge persistedEdge;
@@ -224,13 +243,13 @@ public abstract class AbstractGraphDataService {
             Edge patchedEdge = RelationshipSchemaValidator.validateIncomingPatchPayload(existingEdge, version, edgePayload);
             persistedEdge = updateBulkEdge(patchedEdge, version, txId);
           }
-          
+
 
           Edge outgoingEdge = RelationshipSchemaValidator.validateOutgoingPayload(version, persistedEdge);
           edges.put(item.getKey(), outgoingEdge);
-        } 
-      } 
-      
+        }
+      }
+
       // commit transaction
       dao.commitTransaction(txId);
     } catch (CrudException ex) {
@@ -244,26 +263,32 @@ public abstract class AbstractGraphDataService {
         dao.rollbackTransaction(txId);
       }
     }
-    
+
     return CrudResponseBuilder.buildUpsertBulkResponse(vertices, edges, version, payload);
   }
 
 
-  public abstract String addVertex(String version, String type, VertexPayload payload) throws CrudException;
-  public abstract String updateVertex(String version, String id, String type, VertexPayload payload) throws CrudException;
-  public abstract String patchVertex(String version, String id, String type, VertexPayload payload) throws CrudException;
+  public abstract ImmutablePair<EntityTag, String> addVertex(String version, String type, VertexPayload payload)
+            throws CrudException;
+  public abstract ImmutablePair<EntityTag, String> updateVertex(String version, String id, String type,
+            VertexPayload payload) throws CrudException;
+  public abstract ImmutablePair<EntityTag, String> patchVertex(String version, String id, String type,
+            VertexPayload payload) throws CrudException;
   public abstract String deleteVertex(String version, String id, String type) throws CrudException;
-  public abstract String addEdge(String version, String type, EdgePayload payload) throws CrudException;
+  public abstract ImmutablePair<EntityTag, String> addEdge(String version, String type, EdgePayload payload)
+            throws CrudException;
   public abstract String deleteEdge(String version, String id, String type) throws CrudException;
-  public abstract String updateEdge(String version, String id, String type, EdgePayload payload) throws CrudException;
-  public abstract String patchEdge(String version, String id, String type, EdgePayload payload) throws CrudException;
-  
+  public abstract ImmutablePair<EntityTag, String> updateEdge(String version, String id, String type,
+            EdgePayload payload) throws CrudException;
+  public abstract ImmutablePair<EntityTag, String> patchEdge(String version, String id, String type,
+            EdgePayload payload) throws CrudException;
+
   protected abstract Vertex addBulkVertex(Vertex vertex, String version, String dbTransId) throws CrudException;
   protected abstract Vertex updateBulkVertex(Vertex vertex, String id, String version, String dbTransId) throws CrudException;
   protected abstract void deleteBulkVertex(String id, String version, String type, String dbTransId) throws CrudException;
-  
+
   protected abstract Edge addBulkEdge(Edge edge, String version, String dbTransId) throws CrudException;
   protected abstract Edge updateBulkEdge(Edge edge, String version, String dbTransId) throws CrudException;
   protected abstract void deleteBulkEdge(String id, String version, String type, String dbTransId) throws CrudException;
-  
+
 }
index 5d37acb..6b447a1 100644 (file)
@@ -20,6 +20,8 @@
  */
 package org.onap.crud.service;
 
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
 import java.text.SimpleDateFormat;
 import java.util.HashMap;
 import java.util.Timer;
@@ -32,7 +34,9 @@ import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import javax.annotation.PreDestroy;
+import javax.ws.rs.core.EntityTag;
 import javax.ws.rs.core.Response.Status;
+import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.onap.aai.cl.api.LogFields;
 import org.onap.aai.cl.api.Logger;
 import org.onap.aai.cl.eelf.LoggerFactory;
@@ -40,6 +44,7 @@ import org.onap.aai.cl.mdc.MdcContext;
 import org.onap.aai.cl.mdc.MdcOverride;
 import org.onap.aai.event.api.EventConsumer;
 import org.onap.aai.event.api.EventPublisher;
+import org.onap.aai.restclient.client.OperationResult;
 import org.onap.crud.dao.GraphDao;
 import org.onap.crud.entity.Edge;
 import org.onap.crud.entity.Vertex;
@@ -53,6 +58,7 @@ import org.onap.crud.exception.CrudException;
 import org.onap.crud.logging.CrudServiceMsgs;
 import org.onap.crud.util.CrudProperties;
 import org.onap.crud.util.CrudServiceConstants;
+import org.onap.crud.util.etag.EtagGenerator;
 import org.onap.schema.OxmModelValidator;
 import org.onap.schema.RelationshipSchemaValidator;
 
@@ -71,6 +77,7 @@ public class CrudAsyncGraphDataService extends AbstractGraphDataService {
     private static Logger metricsLogger =
             LoggerFactory.getInstance().getMetricsLogger(CrudAsyncGraphDataService.class.getName());
     private static LogFields okFields = new LogFields();
+    private EtagGenerator etagGenerator;
 
     static {
         okFields.setField(Status.OK, Status.OK.toString());
@@ -83,12 +90,12 @@ public class CrudAsyncGraphDataService extends AbstractGraphDataService {
     }
 
     public CrudAsyncGraphDataService(GraphDao dao, EventPublisher asyncRequestPublisher,
-            EventConsumer asyncResponseConsumer) throws CrudException {
+            EventConsumer asyncResponseConsumer) throws CrudException, NoSuchAlgorithmException {
         this(dao, dao, asyncRequestPublisher, asyncResponseConsumer);
     }
 
     public CrudAsyncGraphDataService(GraphDao dao, GraphDao daoForGet, EventPublisher asyncRequestPublisher,
-            EventConsumer asyncResponseConsumer) throws CrudException {
+            EventConsumer asyncResponseConsumer) throws CrudException, NoSuchAlgorithmException {
 
         super();
         this.dao = dao;
@@ -116,6 +123,7 @@ public class CrudAsyncGraphDataService extends AbstractGraphDataService {
         timer.schedule(crudAsyncResponseConsumer, responsePollInterval, responsePollInterval);
 
         this.asyncRequestPublisher = asyncRequestPublisher;
+        this.etagGenerator = new EtagGenerator();
 
         logger.info(CrudServiceMsgs.ASYNC_DATA_SERVICE_INFO, "CrudAsyncGraphDataService initialized SUCCESSFULLY!");
     }
@@ -199,7 +207,8 @@ public class CrudAsyncGraphDataService extends AbstractGraphDataService {
     }
 
     @Override
-    public String addVertex(String version, String type, VertexPayload payload) throws CrudException {
+    public ImmutablePair<EntityTag, String> addVertex(String version, String type, VertexPayload payload)
+            throws CrudException {
         // Validate the incoming payload
         Vertex vertex = OxmModelValidator.validateIncomingUpsertPayload(null, version, type, payload.getProperties());
         vertex.getProperties().put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type);
@@ -208,41 +217,81 @@ public class CrudAsyncGraphDataService extends AbstractGraphDataService {
                 .vertex(GraphEventVertex.fromVertex(vertex, version)).build();
 
         GraphEventEnvelope response = sendAndWait(event);
-        return responseHandler.handleVertexResponse(version, event, response);
+
+        EntityTag entityTag;
+        try {
+            entityTag = new EntityTag(etagGenerator.computeHashForVertex(response.getBody().getVertex()));
+        } catch (IOException e) {
+            throw new CrudException(e);
+        }
+        String responsePayload = responseHandler.handleVertexResponse(version, event, response);
+
+        return new ImmutablePair<EntityTag, String>(entityTag, responsePayload);
     }
 
     @Override
-    public String addEdge(String version, String type, EdgePayload payload) throws CrudException {
+    public ImmutablePair<EntityTag, String> addEdge(String version, String type, EdgePayload payload)
+            throws CrudException {
         Edge edge = RelationshipSchemaValidator.validateIncomingAddPayload(version, type, payload);
         // Create graph request event
         GraphEvent event =
                 GraphEvent.builder(GraphEventOperation.CREATE).edge(GraphEventEdge.fromEdge(edge, version)).build();
 
         GraphEventEnvelope response = sendAndWait(event);
-        return responseHandler.handleEdgeResponse(version, event, response);
+
+        EntityTag entityTag;
+        try {
+            entityTag = new EntityTag(etagGenerator.computeHashForEdge(response.getBody().getEdge()));
+        } catch (IOException e) {
+            throw new CrudException(e);
+        }
+        String responsePayload = responseHandler.handleEdgeResponse(version, event, response);
+
+        return new ImmutablePair<EntityTag, String>(entityTag, responsePayload);
     }
 
     @Override
-    public String updateVertex(String version, String id, String type, VertexPayload payload) throws CrudException {
+    public ImmutablePair<EntityTag, String> updateVertex(String version, String id, String type, VertexPayload payload)
+            throws CrudException {
         Vertex vertex = OxmModelValidator.validateIncomingUpsertPayload(id, version, type, payload.getProperties());
         GraphEvent event = GraphEvent.builder(GraphEventOperation.UPDATE)
                 .vertex(GraphEventVertex.fromVertex(vertex, version)).build();
 
         GraphEventEnvelope response = sendAndWait(event);
-        return responseHandler.handleVertexResponse(version, event, response);
+
+        EntityTag entityTag;
+        try {
+            entityTag = new EntityTag(etagGenerator.computeHashForVertex(response.getBody().getVertex()));
+        } catch (IOException e) {
+            throw new CrudException(e);
+        }
+        String responsePayload = responseHandler.handleVertexResponse(version, event, response);
+
+        return new ImmutablePair<EntityTag, String>(entityTag, responsePayload);
     }
 
     @Override
-    public String patchVertex(String version, String id, String type, VertexPayload payload) throws CrudException {
-        Vertex existingVertex = dao.getVertex(id, OxmModelValidator.resolveCollectionType(version, type), version,
+    public ImmutablePair<EntityTag, String> patchVertex(String version, String id, String type, VertexPayload payload)
+            throws CrudException {
+        OperationResult existingVertexOpResult = dao.getVertex(id, OxmModelValidator.resolveCollectionType(version, type), version,
                 new HashMap<String, String>());
+        Vertex existingVertex = Vertex.fromJson(existingVertexOpResult.getResult(), version);
         Vertex patchedVertex = OxmModelValidator.validateIncomingPatchPayload(id, version, type,
                 payload.getProperties(), existingVertex);
         GraphEvent event = GraphEvent.builder(GraphEventOperation.UPDATE)
                 .vertex(GraphEventVertex.fromVertex(patchedVertex, version)).build();
 
         GraphEventEnvelope response = sendAndWait(event);
-        return responseHandler.handleVertexResponse(version, event, response);
+
+        EntityTag entityTag;
+        try {
+            entityTag = new EntityTag(etagGenerator.computeHashForVertex(response.getBody().getVertex()));
+        } catch (IOException e) {
+            throw new CrudException(e);
+        }
+        String responsePayload = responseHandler.handleVertexResponse(version, event, response);
+
+        return new ImmutablePair<EntityTag, String>(entityTag, responsePayload);
     }
 
     @Override
@@ -266,25 +315,47 @@ public class CrudAsyncGraphDataService extends AbstractGraphDataService {
     }
 
     @Override
-    public String updateEdge(String version, String id, String type, EdgePayload payload) throws CrudException {
-        Edge edge = dao.getEdge(id, type, new HashMap<String, String>());
+    public ImmutablePair<EntityTag, String> updateEdge(String version, String id, String type, EdgePayload payload)
+            throws CrudException {
+        OperationResult operationResult = dao.getEdge(id, type, new HashMap<String, String>());
+        Edge edge = Edge.fromJson(operationResult.getResult());
         Edge validatedEdge = RelationshipSchemaValidator.validateIncomingUpdatePayload(edge, version, payload);
         GraphEvent event = GraphEvent.builder(GraphEventOperation.UPDATE)
                 .edge(GraphEventEdge.fromEdge(validatedEdge, version)).build();
 
         GraphEventEnvelope response = sendAndWait(event);
-        return responseHandler.handleEdgeResponse(version, event, response);
+
+        EntityTag entityTag;
+        try {
+            entityTag = new EntityTag(etagGenerator.computeHashForEdge(response.getBody().getEdge()));
+        } catch (IOException e) {
+            throw new CrudException(e);
+        }
+        String responsePayload = responseHandler.handleEdgeResponse(version, event, response);
+
+        return new ImmutablePair<EntityTag, String>(entityTag, responsePayload);
     }
 
     @Override
-    public String patchEdge(String version, String id, String type, EdgePayload payload) throws CrudException {
-        Edge edge = dao.getEdge(id, type, new HashMap<String, String>());
+    public ImmutablePair<EntityTag, String> patchEdge(String version, String id, String type, EdgePayload payload)
+            throws CrudException {
+        OperationResult operationResult = dao.getEdge(id, type, new HashMap<String, String>());
+        Edge edge = Edge.fromJson(operationResult.getResult());
         Edge patchedEdge = RelationshipSchemaValidator.validateIncomingPatchPayload(edge, version, payload);
         GraphEvent event = GraphEvent.builder(GraphEventOperation.UPDATE)
                 .edge(GraphEventEdge.fromEdge(patchedEdge, version)).build();
 
         GraphEventEnvelope response = sendAndWait(event);
-        return responseHandler.handleEdgeResponse(version, event, response);
+
+        EntityTag entityTag;
+        try {
+            entityTag = new EntityTag(etagGenerator.computeHashForEdge(response.getBody().getEdge()));
+        } catch (IOException e) {
+            throw new CrudException(e);
+        }
+        String responsePayload = responseHandler.handleEdgeResponse(version, event, response);
+
+        return new ImmutablePair<EntityTag, String>(entityTag, responsePayload);
     }
 
     @PreDestroy
@@ -349,4 +420,4 @@ public class CrudAsyncGraphDataService extends AbstractGraphDataService {
         responseHandler.handleBulkEventResponse(event, response);
         return response.getBody();
     }
-}
+}
\ No newline at end of file
index 5a2710d..5b1c2dd 100644 (file)
@@ -22,20 +22,22 @@ package org.onap.crud.service;
 
 
 import java.util.HashMap;
-
+import javax.ws.rs.core.EntityTag;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.onap.aai.restclient.client.OperationResult;
 import org.onap.crud.dao.GraphDao;
 import org.onap.crud.entity.Edge;
-
 import org.onap.crud.entity.Vertex;
 import org.onap.crud.exception.CrudException;
 import org.onap.crud.parser.CrudResponseBuilder;
+import org.onap.crud.util.CrudServiceUtil;
 import org.onap.schema.OxmModelValidator;
 import org.onap.schema.RelationshipSchemaValidator;
 
 
 public class CrudGraphDataService extends AbstractGraphDataService {
-  
+
+
   public CrudGraphDataService(GraphDao dao) throws CrudException {
     super();
     this.dao = dao;
@@ -48,103 +50,146 @@ public class CrudGraphDataService extends AbstractGraphDataService {
     this.daoForGet = daoForGet;
   }
 
-  public String addVertex(String version, String type, VertexPayload payload) throws CrudException {
+  @Override
+  public ImmutablePair<EntityTag, 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(), version);
-    return CrudResponseBuilder
+  private ImmutablePair<EntityTag, String> addVertex(String version, Vertex vertex) throws CrudException {
+    OperationResult addedVertexResult = dao.addVertex(vertex.getType(), vertex.getProperties(), version);
+    EntityTag entityTag = CrudServiceUtil.getETagFromHeader(addedVertexResult.getHeaders());
+    Vertex addedVertex = Vertex.fromJson(addedVertexResult.getResult(), version);
+    String payload = CrudResponseBuilder
         .buildUpsertVertexResponse(OxmModelValidator.validateOutgoingPayload(version, addedVertex), version);
+
+    return new ImmutablePair<EntityTag, String>(entityTag, payload);
   }
 
-  public String addEdge(String version, String type, EdgePayload payload) throws CrudException {
+  @Override
+  public ImmutablePair<EntityTag, 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(), version);
-    return CrudResponseBuilder
-        .buildUpsertEdgeResponse(RelationshipSchemaValidator.validateOutgoingPayload(version, addedEdge), version);
+  private ImmutablePair<EntityTag, String> addEdge(String version, Edge edge) throws CrudException {
+    OperationResult addedEdgeResult = dao.addEdge(edge.getType(), edge.getSource(), edge.getTarget(), edge.getProperties(), version);
+    EntityTag entityTag = CrudServiceUtil.getETagFromHeader(addedEdgeResult.getHeaders());
+    Edge addedEdge = Edge.fromJson(addedEdgeResult.getResult());
+    String payload = CrudResponseBuilder
+      .buildUpsertEdgeResponse(RelationshipSchemaValidator.validateOutgoingPayload(version, addedEdge), version);
+
+    return new ImmutablePair<EntityTag, String>(entityTag, payload);
   }
 
-  public String updateVertex(String version, String id, String type, VertexPayload payload) throws CrudException {
+  @Override
+  public ImmutablePair<EntityTag, 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 ImmutablePair<EntityTag, String> updateVertex(String version, Vertex vertex) throws CrudException {
+    OperationResult updatedVertexResult = dao.updateVertex(vertex.getId().get(), vertex.getType(), vertex.getProperties(), version);
+    String payload = getUpdatedVertexPayload(version, updatedVertexResult);
+    EntityTag entityTag = CrudServiceUtil.getETagFromHeader(updatedVertexResult.getHeaders());
+
+    return new ImmutablePair<EntityTag, String>(entityTag, payload);
   }
 
-  private String updateVertex(String version, Vertex vertex) throws CrudException {
-    Vertex updatedVertex = dao.updateVertex(vertex.getId().get(), vertex.getType(), vertex.getProperties(), version);
+  private String getUpdatedVertexPayload(String version, OperationResult updatedVertexResult) throws CrudException {
+    Vertex updatedVertex = Vertex.fromJson(updatedVertexResult.getResult(), version);
+
     return CrudResponseBuilder
-        .buildUpsertVertexResponse(OxmModelValidator.validateOutgoingPayload(version, updatedVertex), version);
+      .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), version, new HashMap<String, String>());
+  @Override
+  public ImmutablePair<EntityTag, String> patchVertex(String version, String id, String type, VertexPayload payload)
+            throws CrudException {
+    OperationResult existingVertexOpResult = dao.getVertex(id, OxmModelValidator.resolveCollectionType(version, type), version, new HashMap<String, String>());
+    Vertex existingVertex = Vertex.fromJson(existingVertexOpResult.getResult(), version);
     Vertex vertex = OxmModelValidator.validateIncomingPatchPayload(id, version, type, payload.getProperties(),
-        existingVertex);
+          existingVertex);
     return updateVertex(version, vertex);
   }
 
+  @Override
   public String deleteVertex(String version, String id, String type) throws CrudException {
     type = OxmModelValidator.resolveCollectionType(version, type);
     dao.deleteVertex(id, type);
     return "";
   }
 
+  @Override
   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, new HashMap<String, String>());
-    Edge validatedEdge = RelationshipSchemaValidator.validateIncomingUpdatePayload(edge, version, payload);
+  @Override
+  public ImmutablePair<EntityTag, String> updateEdge(String version, String id, String type, EdgePayload payload)
+            throws CrudException {
+    Edge validatedEdge = getValidatedEdge(version, id, type, payload);
     return updateEdge(version, validatedEdge);
   }
 
-  private String updateEdge(String version, Edge edge) throws CrudException {
-    Edge updatedEdge = dao.updateEdge(edge);
+  private ImmutablePair<EntityTag, String> updateEdge(String version, Edge edge) throws CrudException {
+    OperationResult updatedEdgeResult = dao.updateEdge(edge);
+    String payload = getUpdatedEdgePayload(version, updatedEdgeResult);
+    EntityTag entityTag = CrudServiceUtil.getETagFromHeader(updatedEdgeResult.getHeaders());
+
+    return new ImmutablePair<EntityTag, String>(entityTag, payload);
+  }
+
+  private String getUpdatedEdgePayload(String version, OperationResult updatedEdgeResult) throws CrudException {
+    Edge updatedEdge = Edge.fromJson(updatedEdgeResult.getResult());
+
     return CrudResponseBuilder
-        .buildUpsertEdgeResponse(RelationshipSchemaValidator.validateOutgoingPayload(version, updatedEdge), version);
+      .buildUpsertEdgeResponse(RelationshipSchemaValidator.validateOutgoingPayload(version, updatedEdge), version);
+  }
+
+  private Edge getValidatedEdge(String version, String id, String type, EdgePayload payload) throws CrudException {
+      OperationResult operationResult = dao.getEdge(id, type, new HashMap<String, String>());
+    return RelationshipSchemaValidator.validateIncomingUpdatePayload(Edge.fromJson(operationResult.getResult()), version, payload);
   }
-  
-  public String patchEdge(String version, String id, String type, EdgePayload payload) throws CrudException {
-    Edge edge = dao.getEdge(id, type, new HashMap<String, String>());
-    Edge patchedEdge = RelationshipSchemaValidator.validateIncomingPatchPayload(edge, version, payload);
-    return updateEdge(version, patchedEdge);
 
+  @Override
+  public ImmutablePair<EntityTag, String> patchEdge(String version, String id, String type, EdgePayload payload)
+            throws CrudException {
+    OperationResult operationResult = dao.getEdge(id, type, new HashMap<String, String>());
+    Edge patchedEdge = RelationshipSchemaValidator.validateIncomingPatchPayload(Edge.fromJson(operationResult.getResult()), version, payload);
+    return updateEdge(version, patchedEdge);
   }
 
   @Override
   protected Vertex addBulkVertex(Vertex vertex, String version, String dbTransId) throws CrudException {
     return dao.addVertex(vertex.getType(), vertex.getProperties(), version, dbTransId);
   }
-  
+
   @Override
   protected Vertex updateBulkVertex(Vertex vertex, String id, String version, String dbTransId) throws CrudException {
     return dao.updateVertex(id, vertex.getType(), vertex.getProperties(), version, dbTransId);
   }
-  
+
   @Override
   protected void deleteBulkVertex(String id, String version, String type, String dbTransId) throws CrudException {
     dao.deleteVertex(id, type, dbTransId);
   }
-  
+
   @Override
   protected Edge addBulkEdge(Edge edge, String version, String dbTransId) throws CrudException {
     return dao.addEdge(edge.getType(), edge.getSource(), edge.getTarget(), edge.getProperties(), version, dbTransId);
   }
-  
+
   @Override
   protected Edge updateBulkEdge(Edge edge, String version, String dbTransId) throws CrudException {
     return dao.updateEdge(edge, dbTransId);
   }
-  
+
   @Override
   protected void deleteBulkEdge(String id, String version, String type, String dbTransId) throws CrudException {
     dao.deleteEdge(id, type, dbTransId);
index 2cbb87c..583fee6 100644 (file)
@@ -38,11 +38,13 @@ 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.EntityTag;
 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;
+import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.cxf.jaxrs.ext.PATCH;
 import org.onap.aai.cl.api.Logger;
 import org.onap.aai.cl.eelf.LoggerFactory;
@@ -102,8 +104,8 @@ public class CrudRestService {
 
     try {
       if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
-          String result = graphDataService.getVertex(version, id, type, params);
-        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+          ImmutablePair<EntityTag, String> result = graphDataService.getVertex(version, id, type, params);
+        response = Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
@@ -142,8 +144,8 @@ public class CrudRestService {
           properties = new HashSet<>();
         }
 
-        String result = graphDataService.getVertices(version, type, filter, properties);
-        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+        ImmutablePair<EntityTag, String> result = graphDataService.getVertices(version, type, filter, properties);
+        response = Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
@@ -175,8 +177,8 @@ public class CrudRestService {
     try {
       if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
 
-        String result = graphDataService.getEdge(version, id, type, params);
-        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+        ImmutablePair<EntityTag, String> result = graphDataService.getEdge(version, id, type, params);
+        response = Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
@@ -206,8 +208,8 @@ public class CrudRestService {
 
     try {
       if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
-        String result = graphDataService.getEdges(version, type, filter);
-        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+          ImmutablePair<EntityTag, String> result = graphDataService.getEdges(version, type, filter);
+          response = Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
@@ -244,17 +246,16 @@ public class CrudRestService {
         if (payload.getId() != null && !payload.getId().equals(id)) {
           throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
         }
-        String result;
-
+        ImmutablePair<EntityTag, String> result;
         if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null
             && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE).equalsIgnoreCase("PATCH")) {
           result = graphDataService.patchEdge(version, id, type, payload);
+          response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
         } else {
-
           result = graphDataService.updateEdge(version, id, type, payload);
+          response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
         }
-
-        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+        
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
@@ -291,8 +292,8 @@ public class CrudRestService {
           throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
         }
 
-        String result = graphDataService.patchEdge(version, id, type, payload);
-        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+        ImmutablePair<EntityTag, String> result = graphDataService.patchEdge(version, id, type, payload);
+        response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
@@ -319,7 +320,6 @@ public class CrudRestService {
     logger.debug("Incoming request..." + content);
     Response response = null;
 
-
     try {
       if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
         VertexPayload payload = VertexPayload.fromJson(content);
@@ -330,18 +330,18 @@ public class CrudRestService {
           throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
         }
 
-        String result;
-
         payload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, false));
 
+        ImmutablePair<EntityTag, String> result;
         if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null
             && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE).equalsIgnoreCase("PATCH")) {
           result = graphDataService.patchVertex(version, id, type, payload);
+          response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
         } else {
-
           result = graphDataService.updateVertex(version, id, type, payload);
+          response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
         }
-        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+        
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
@@ -380,8 +380,8 @@ public class CrudRestService {
 
         payload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, false));
 
-        String result = graphDataService.patchVertex(version, id, type, payload);
-        response = Response.status(Status.OK).entity(result).type(mediaType).build();
+        ImmutablePair<EntityTag, String> result = graphDataService.patchVertex(version, id, type, payload);
+        response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
@@ -425,8 +425,8 @@ public class CrudRestService {
 
         payload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, true));
 
-        String result = graphDataService.addVertex(version, type, payload);
-        response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+        ImmutablePair<EntityTag, String> result = graphDataService.addVertex(version, type, payload);
+        response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
@@ -612,8 +612,8 @@ public class CrudRestService {
 
         payload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, true));
 
-        String result = graphDataService.addVertex(version, payload.getType(), payload);
-        response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+        ImmutablePair<EntityTag, String> result = graphDataService.addVertex(version, payload.getType(), payload);
+        response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
@@ -654,8 +654,8 @@ public class CrudRestService {
         if (payload.getType() != null && !payload.getType().equals(type)) {
           throw new CrudException("Edge Type mismatch", Status.BAD_REQUEST);
         }
-        String result = graphDataService.addEdge(version, type, payload);
-        response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+        ImmutablePair<EntityTag, String> result = graphDataService.addEdge(version, type, payload);
+        response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
@@ -695,9 +695,8 @@ public class CrudRestService {
         if (payload.getType() == null || payload.getType().isEmpty()) {
           throw new CrudException("Missing Edge Type ", Status.BAD_REQUEST);
         }
-        String result = graphDataService.addEdge(version, payload.getType(), payload);
-
-        response = Response.status(Status.CREATED).entity(result).type(mediaType).build();
+        ImmutablePair<EntityTag, String> result = graphDataService.addEdge(version, payload.getType(), payload);
+        response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
       } else {
         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
       }
index 630ec02..a670b54 100644 (file)
  */
 package org.onap.crud.service;
 
+import javax.ws.rs.core.Response.Status;
+import org.onap.crud.exception.CrudException;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.JsonElement;
 
-import org.onap.crud.exception.CrudException;
-
-import javax.ws.rs.core.Response.Status;
-
 public class EdgePayload {
 
   private String id;
index 594dc1a..172d03b 100644 (file)
  */
 package org.onap.crud.service;
 
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonElement;
-
-import org.onap.crud.exception.CrudException;
-
 import java.util.ArrayList;
 import java.util.List;
 import javax.ws.rs.core.Response.Status;
+import org.onap.crud.exception.CrudException;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
 
 public class VertexPayload {
 
index 3a02852..ae5b464 100644 (file)
@@ -39,4 +39,5 @@ public class CrudServiceConstants {
   public static final String CRD_COLLECTION_PROPERTIES_KEY = "crud.collection.properties.key";
   public static final String CRD_RESERVED_VERSION = "_reserved_version";
   public static final String CRD_RESERVED_NODE_TYPE = "_reserved_aai-type";
+  public static final String CRD_HEADER_ETAG = "etag";
 }
index 6c251bc..6b5cdcd 100644 (file)
  */
 package org.onap.crud.util;
 
+import java.util.AbstractMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response.Status;
 import org.onap.aai.db.props.AAIProperties;
 import org.onap.crud.exception.CrudException;
 import org.onap.schema.OxmModelLoader;
@@ -29,15 +38,6 @@ import com.google.gson.Gson;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonPrimitive;
 
-import java.util.AbstractMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.Map.Entry;
-
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.Response.Status;
-
 public class CrudServiceUtil {
 
   private static Gson gson = new Gson();
@@ -54,13 +54,13 @@ public class CrudServiceUtil {
       } else if (clazz.isAssignableFrom(Double.class)) {
         return Double.parseDouble(value);
       } else if (clazz.isAssignableFrom(Boolean.class)) {
-                 
+
                // If the value is an IN/OUT direction, this gets seen as a boolean, so
         // check for that first.
         if (value.equals("OUT") || value.equals("IN")) {
           return value;
         }
-               
+
         if (!value.equals("true") && !value.equals("false")) {
           throw new CrudException("Invalid propertry value: " + value, Status.BAD_REQUEST);
         }
@@ -82,7 +82,7 @@ public class CrudServiceUtil {
       throw new CrudException(e);
     }
   }
-  
+
   /**
    * This method will merge header property from app id in request payload if not already populated
    * @param propertiesFromRequest
@@ -104,12 +104,12 @@ public class CrudServiceUtil {
     
     if(!propertyKeys.contains(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH)) {
         properties.add(new AbstractMap.SimpleEntry<String, JsonElement>(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH,
-            (JsonElement)(new JsonPrimitive(sourceOfTruth))));
+            (new JsonPrimitive(sourceOfTruth))));
     }
    
     if(isAdd && !propertyKeys.contains(AAIProperties.SOURCE_OF_TRUTH)) {
         properties.add(new AbstractMap.SimpleEntry<String, JsonElement>(AAIProperties.SOURCE_OF_TRUTH,
-            (JsonElement)(new JsonPrimitive(sourceOfTruth))));
+            (new JsonPrimitive(sourceOfTruth))));
     }
 
     Object[] propArray = properties.toArray();
@@ -117,7 +117,7 @@ public class CrudServiceUtil {
     sb.append("{");
     boolean first=true;
     for(int i=0; i<propArray.length; i++) {
-      
+
       Map.Entry<String, JsonElement> entry = (Entry<String, JsonElement>) propArray[i];
       if(!first) {
         sb.append(",");
@@ -126,7 +126,17 @@ public class CrudServiceUtil {
       first=false;
     }
     sb.append("}");
-    
+
     return gson.fromJson(sb.toString(), JsonElement.class);
   }
+
+  public static EntityTag getETagFromHeader(MultivaluedMap<String, String> headers) {
+    EntityTag entityTag = null;
+    if (headers != null && headers.containsKey(CrudServiceConstants.CRD_HEADER_ETAG)) {
+      String value = headers.getFirst(CrudServiceConstants.CRD_HEADER_ETAG);
+      entityTag = new EntityTag(value.replace("\"", ""));
+    }
+    return entityTag;
+  }
+
 }
diff --git a/src/main/java/org/onap/crud/util/HashGenerator.java b/src/main/java/org/onap/crud/util/HashGenerator.java
new file mode 100644 (file)
index 0000000..02558fa
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017-2018 Amdocs
+ * ================================================================================
+ * 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=========================================================
+ */
+package org.onap.crud.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+/**
+ * Generates a sha 256 hash
+ */
+public class HashGenerator {
+
+    private MessageDigest messageDigest;
+
+    public HashGenerator() throws NoSuchAlgorithmException {
+        this.messageDigest = MessageDigest.getInstance("SHA-256");
+    }
+
+    /**
+     * Generates a SHA 256 hash as a hexadecimal string for the inputs.
+     * Calls toString on the input objects to convert into a byte stream.
+     * @param values
+     * @return SHA 256 hash of the inputs as a hexadecimal string.
+     * @throws IOException
+     */
+    public String generateSHA256AsHex(Object... values) throws IOException {
+        byte[] bytes = convertToBytes(values);
+        byte[] digest = messageDigest.digest(bytes);
+        StringBuilder result = new StringBuilder();
+        for (byte byt : digest) result.append(Integer.toString((byt & 0xff) + 0x100, 16).substring(1));
+        return result.toString();
+    }
+
+    private byte[] convertToBytes(Object... values) throws IOException {
+        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
+             ObjectOutput out = new ObjectOutputStream(bos)) {
+            for (Object object : values) {
+                out.writeObject(object.toString());
+            }
+            return bos.toByteArray();
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/onap/crud/util/etag/EtagGenerator.java b/src/main/java/org/onap/crud/util/etag/EtagGenerator.java
new file mode 100644 (file)
index 0000000..b288b78
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017-2018 Amdocs
+ * ================================================================================
+ * 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=========================================================
+ */
+package org.onap.crud.util.etag;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.onap.crud.event.GraphEventEdge;
+import org.onap.crud.event.GraphEventVertex;
+import org.onap.crud.util.HashGenerator;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+/**
+ * Computes hash for GraphEventVertex and GraphEventEdge
+ */
+public class EtagGenerator {
+
+    private static final String AAI_LAST_MOD_TS = "aai-last-mod-ts";
+    private final HashGenerator hashGenerator;
+
+    public EtagGenerator() throws NoSuchAlgorithmException {
+        this.hashGenerator = new HashGenerator();
+    }
+
+    /**
+     * Takes in the GraphEventVertex for which the hash is to be computed.
+     * @param GraphEventVertex
+     * @return hash for the GraphEventVertex
+     * @throws IOException
+     */
+    public String computeHashForVertex(GraphEventVertex graphEventVertex) throws IOException {
+        return hashGenerator.generateSHA256AsHex(graphEventVertex.getId(), graphEventVertex.getType(), convertPropertiesToMap(graphEventVertex.getProperties()));
+    }
+
+    /**
+     * Takes in the GraphEventEdge for which the hash is to be computed.
+     * @param GraphEventEdge
+     * @return hash for the GraphEventEdge
+     * @throws IOException
+     */
+    public String computeHashForEdge(GraphEventEdge graphEventEdge) throws IOException {
+        return hashGenerator.generateSHA256AsHex(graphEventEdge.getId(), graphEventEdge.getType(),
+                convertPropertiesToMap(graphEventEdge.getProperties()),
+                computeHashForVertex(graphEventEdge.getSource()), computeHashForVertex(graphEventEdge.getTarget()));
+    }
+
+    private Map<String, Object> convertPropertiesToMap(JsonElement properties) {
+        Map<String, Object> propertiesMap = new HashMap<>();
+        if (null != properties) {
+            JsonObject propsObject = properties.getAsJsonObject();
+            for (Entry<String, JsonElement> props : propsObject.entrySet()) {
+                String key = props.getKey();
+                String value = props.getValue().getAsString();
+                propertiesMap.put(key, value);
+            }
+        }
+        return filterAndSortProperties(propertiesMap);
+    }
+
+    private Map<String, Object> filterAndSortProperties(Map<String, Object> properties) {
+        return properties
+                .entrySet()
+                .stream()
+                .filter(x -> !x.getKey().equals(AAI_LAST_MOD_TS))
+                .sorted((x, y) -> x.getKey().compareTo(y.getKey()))
+                .collect(LinkedHashMap::new,
+                        (m, e) -> m.put(e.getKey(), e.getValue()),
+                        Map::putAll);
+    }
+}
\ No newline at end of file
index 13cba11..68c876c 100644 (file)
@@ -26,8 +26,6 @@ import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
@@ -37,8 +35,8 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.mockito.Mockito;
-import org.onap.crud.dao.TestDao;
 import org.onap.crud.exception.CrudException;
+import org.onap.crud.service.TestDao;
 import org.onap.crud.service.util.TestHeaders;
 import org.onap.crud.service.util.TestRequest;
 import org.onap.crud.service.util.TestUriInfo;
@@ -80,18 +78,18 @@ public class CrudRestServiceTest {
 
   @Before
   public void init() throws Exception {
-      Path resourcePath = Paths.get(ClassLoader.getSystemResource("model").toURI());
-      Path parentPath = resourcePath.getParent();
+    ClassLoader classLoader = getClass().getClassLoader();
+    File dir = new File(classLoader.getResource("model").getFile());
+    System.setProperty("CONFIG_HOME", dir.getParent());
+    EdgeRulesLoader.resetSchemaVersionContext();
 
-      System.setProperty("CONFIG_HOME", parentPath.toString());
-    EdgeRulesLoader.resetSchemaVersionContext ();
-      CrudGraphDataService service = new CrudGraphDataService(new TestDao());
-      CrudRestService restService = new CrudRestService(service, null);
-      mockService = Mockito.spy(restService);
-      
-      Mockito.doReturn(true).when(mockService).validateRequest(Mockito.any(HttpServletRequest.class), 
-          Mockito.anyString(), Mockito.anyString(), Mockito.any(CrudRestService.Action.class), Mockito.anyString(), 
-          Mockito.any(HttpHeaders.class));
+    CrudGraphDataService service = new CrudGraphDataService(new TestDao());
+    CrudRestService restService = new CrudRestService(service, null);
+    mockService = Mockito.spy(restService);
+
+    Mockito.doReturn(true).when(mockService).validateRequest(Mockito.any(HttpServletRequest.class),
+        Mockito.anyString(), Mockito.anyString(), Mockito.any(CrudRestService.Action.class), Mockito.anyString(),
+        Mockito.any(HttpHeaders.class));
   }
   
   @Test
@@ -123,21 +121,25 @@ public class CrudRestServiceTest {
         new TestHeaders(), null, new TestRequest());
     System.out.println("Response: " + response.getStatus() + "\n" + response.getEntity().toString());
     assertTrue(response.getStatus() == 400);
+       Assert.assertNull(response.getEntityTag());
     
     response = mockService.addVertex(postVertexPayload, "v11", "services/inventory/v11", 
         new TestHeaders(), null, new TestRequest());
     System.out.println("Response: " + response.getStatus() + "\n" + response.getEntity().toString());
     assertTrue(response.getStatus() == 201);
+       Assert.assertEquals(response.getEntityTag().getValue(), "test123");
     
     response = mockService.addVertex(postMissingPropVertexPayload, "v11", "pserver", "services/inventory/v11", 
         new TestHeaders(), null, new TestRequest());
     System.out.println("Response: " + response.getStatus() + "\n" + response.getEntity().toString());
-    assertTrue(response.getStatus() == 400); 
+    assertTrue(response.getStatus() == 400);
+       Assert.assertNull(response.getEntityTag());
     
     response = mockService.addVertex(postVertexPayload, "v11", "pserver", "services/inventory/v11", 
         new TestHeaders(), null, new TestRequest());
     System.out.println("Response: " + response.getStatus() + "\n" + response.getEntity().toString());
-    assertTrue(response.getStatus() == 201);   
+    assertTrue(response.getStatus() == 201);
+       Assert.assertEquals(response.getEntityTag().getValue(), "test123");
   }
   
   @Test
@@ -148,11 +150,13 @@ public class CrudRestServiceTest {
         new TestHeaders(), null, new TestRequest());
     System.out.println("Response: " + response.getStatus() + "\n" + response.getEntity().toString());
     assertTrue(response.getStatus() == 201);
+       Assert.assertEquals(response.getEntityTag().getValue(), "test123");
     
     response = mockService.addEdge(postEdgePayload, "v11", "tosca.relationships.HostedOn", "services/inventory/v11", 
         new TestHeaders(), null, new TestRequest());
     System.out.println("Response: " + response.getStatus() + "\n" + response.getEntity().toString());
-    assertTrue(response.getStatus() == 201);   
+    assertTrue(response.getStatus() == 201);
+       Assert.assertEquals(response.getEntityTag().getValue(), "test123");
   }
   
   @Test
@@ -170,19 +174,22 @@ public class CrudRestServiceTest {
     response = mockService.updateVertex(putVertexPayload, "v11", "pserver", "bad-id", 
         "services/inventory/v11", new TestHeaders(), null, new TestRequest());
     System.out.println("Response: " + response.getStatus() + "\n" + response.getEntity().toString());
-    assertTrue(response.getStatus() == 400);  
+    assertTrue(response.getStatus() == 400);
+    Assert.assertNull(response.getEntityTag());        
     
     // Success case
     response = mockService.updateVertex(putVertexPayload, "v11", "pserver", "test-uuid", "services/inventory/v11",
                 new TestHeaders(), null, new TestRequest());
     System.out.println("Response: " + response.getStatus() + "\n" + response.getEntity().toString());
     assertTrue(response.getStatus() == 200);  
+       Assert.assertEquals(response.getEntityTag().getValue(), "test123");
     
     // Patch
     response = mockService.patchVertex(putVertexPayload, "v11", "pserver", "test-uuid", 
         "services/inventory/v11", new TestHeaders(), null, new TestRequest());
     System.out.println("Response: " + response.getStatus() + "\n" + response.getEntity().toString());
     assertTrue(response.getStatus() == 200);  
+       Assert.assertEquals(response.getEntityTag().getValue(), "test123");
   }
   
   @Test
@@ -193,12 +200,14 @@ public class CrudRestServiceTest {
         "services/inventory/v11", new TestHeaders(), null, new TestRequest());
     System.out.println("Response: " + response.getStatus() + "\n" + response.getEntity().toString());
     assertTrue(response.getStatus() == 200);  
+       Assert.assertEquals(response.getEntityTag().getValue(), "test123");
     
     // Patch
     response = mockService.patchEdge(postEdgePayload, "v11", "tosca.relationships.HostedOn", "my-uuid", 
         "services/inventory/v11", new TestHeaders(), null, new TestRequest());
     System.out.println("Response: " + response.getStatus() + "\n" + response.getEntity().toString());
     assertTrue(response.getStatus() == 200);
+       Assert.assertEquals(response.getEntityTag().getValue(), "test123");
   }
   
   @Test
  * limitations under the License.
  * ============LICENSE_END=========================================================
  */
-package org.onap.crud.dao;
+package org.onap.crud.service;
 
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import org.onap.aai.restclient.client.OperationResult;
 import org.onap.crud.dao.GraphDao;
 import org.onap.crud.entity.Edge;
 import org.onap.crud.entity.Vertex;
 import org.onap.crud.exception.CrudException;
 
 public class TestDao implements GraphDao {
-  
+
   private final String champVertex = "{" +
       "\"key\": \"test-uuid\"," +
       "\"type\": \"pserver\"," +
       "\"properties\": {" +
       "\"fqdn\": \"myhost.onap.com\"," +
       "\"hostname\": \"myhost\" } }";
-  
+
+  private final String champVertices = "[ {" +
+          "\"key\": \"test-uuid\"," +
+          "\"type\": \"pserver\"," +
+          "\"properties\": {" +
+          "\"fqdn\": \"myhost.onap.com\"," +
+          "\"hostname\": \"myhost\" } } ]";
+
   private final String champEdge = "{" +
       "\"key\": \"test-uuid\"," +
       "\"type\": \"tosca.relationships.HostedOn\"," +
@@ -50,15 +59,28 @@ public class TestDao implements GraphDao {
       "\"key\": \"1d326bc7-b985-492b-9604-0d5d1f06f908\", \"type\": \"pserver\"}" +
       " }";
 
+  private final String champEdges = "[ {" +
+          "\"key\": \"test-uuid\"," +
+          "\"type\": \"tosca.relationships.HostedOn\"," +
+          "\"properties\": {" +
+          "\"prevent-delete\": \"NONE\" }," +
+          "\"source\": {" +
+          "\"key\": \"50bdab41-ad1c-4d00-952c-a0aa5d827811\", \"type\": \"vserver\"}," +
+          "\"target\": {" +
+          "\"key\": \"1d326bc7-b985-492b-9604-0d5d1f06f908\", \"type\": \"pserver\"}" +
+          " } ]";
+
   @Override
   public Vertex getVertex(String id, String version) throws CrudException {
     return Vertex.fromJson(champVertex, "v11");
   }
 
   @Override
-  public Vertex getVertex(String id, String type, String version, Map<String, String> queryParams)
+  public OperationResult getVertex(String id, String type, String version, Map<String, String> queryParams)
       throws CrudException {
-    return Vertex.fromJson(champVertex, "v11");
+    OperationResult operationResult = new OperationResult();
+    operationResult.setResult(champVertex);
+    return operationResult;
   }
 
   @Override
@@ -69,41 +91,49 @@ public class TestDao implements GraphDao {
   }
 
   @Override
-  public List<Vertex> getVertices(String type, Map<String, Object> filter, String version) throws CrudException {
-    List<Vertex> list = new ArrayList<Vertex>();
-    list.add(Vertex.fromJson(champVertex, "v11"));
-    return list;
+  public OperationResult getVertices(String type, Map<String, Object> filter, String version) throws CrudException {
+      OperationResult operationResult = new OperationResult();
+      operationResult.setResult(champVertices);
+      return operationResult;
   }
 
   @Override
-  public List<Vertex> getVertices(String type, Map<String, Object> filter, HashSet<String> properties, String version)
+  public OperationResult getVertices(String type, Map<String, Object> filter, HashSet<String> properties, String version)
       throws CrudException {
-    List<Vertex> list = new ArrayList<Vertex>();
-    list.add(Vertex.fromJson(champVertex, "v11"));
-    return list;
+    OperationResult operationResult = new OperationResult();
+    operationResult.setResult(champVertices);
+    return operationResult;
   }
 
   @Override
-  public Edge getEdge(String id, String type, Map<String, String> queryParams) throws CrudException {
-    return Edge.fromJson(champEdge);
+  public OperationResult getEdge(String id, String type, Map<String, String> queryParams) throws CrudException {
+    OperationResult operationResult = new OperationResult();
+    operationResult.setResult(champEdge);
+    return operationResult;
   }
 
   @Override
-  public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException {
-    List<Edge> list = new ArrayList<Edge>();
-    list.add(Edge.fromJson(champEdge));
-    return list;
+  public OperationResult getEdges(String type, Map<String, Object> filter) throws CrudException {
+      OperationResult operationResult = new OperationResult();
+      operationResult.setResult(champEdges);
+      return operationResult;
   }
 
   @Override
-  public Vertex addVertex(String type, Map<String, Object> properties, String version) throws CrudException {
-    return Vertex.fromJson(champVertex, "v11");
+  public OperationResult addVertex(String type, Map<String, Object> properties, String version) throws CrudException {
+    OperationResult operationResult = new OperationResult();
+    operationResult.setHeaders(addReponseHeader());
+    operationResult.setResult(champVertex);
+    return operationResult;
   }
 
   @Override
-  public Vertex updateVertex(String id, String type, Map<String, Object> properties, String version)
+  public OperationResult updateVertex(String id, String type, Map<String, Object> properties, String version)
       throws CrudException {
-    return Vertex.fromJson(champVertex, "v11");
+    OperationResult operationResult = new OperationResult();
+       operationResult.setHeaders(addReponseHeader());
+    operationResult.setResult(champVertex);
+    return operationResult;
   }
 
   @Override
@@ -112,14 +142,20 @@ public class TestDao implements GraphDao {
   }
 
   @Override
-  public Edge addEdge(String type, Vertex source, Vertex target, Map<String, Object> properties, String version)
+  public OperationResult addEdge(String type, Vertex source, Vertex target, Map<String, Object> properties, String version)
       throws CrudException {
-    return Edge.fromJson(champEdge);
+    OperationResult operationResult = new OperationResult();
+       operationResult.setHeaders(addReponseHeader());
+    operationResult.setResult(champEdge);
+    return operationResult;
   }
 
   @Override
-  public Edge updateEdge(Edge edge) throws CrudException {
-    return Edge.fromJson(champEdge);
+  public OperationResult updateEdge(Edge edge) throws CrudException {
+    OperationResult operationResult = new OperationResult();
+       operationResult.setHeaders(addReponseHeader());
+    operationResult.setResult(champEdge);
+    return operationResult;
   }
 
   @Override
@@ -190,5 +226,10 @@ public class TestDao implements GraphDao {
   public Edge getEdge(String id, String type, String txId) throws CrudException {
     return Edge.fromJson(champEdge);
   }
-
-}
+  
+  private MultivaluedMap<String, String> addReponseHeader() {
+    MultivaluedMap<String, String> headers = new MultivaluedHashMap<String, String>();
+    headers.add("etag", "test123");
+    return headers;
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/org/onap/crud/util/etag/EtagGeneratorTest.java b/src/test/java/org/onap/crud/util/etag/EtagGeneratorTest.java
new file mode 100644 (file)
index 0000000..c4f7c38
--- /dev/null
@@ -0,0 +1,168 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017-2018 Amdocs
+ * ================================================================================
+ * 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=========================================================
+ */
+package org.onap.crud.util.etag;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.crud.event.GraphEventEdge;
+import org.onap.crud.event.GraphEventVertex;
+import org.onap.crud.util.etag.EtagGenerator;
+import com.google.gson.JsonObject;
+
+public class EtagGeneratorTest {
+
+    private EtagGenerator etagGenerator;
+
+    @Before
+    public void init() throws NoSuchAlgorithmException {
+        etagGenerator = new EtagGenerator();
+    }
+
+    private GraphEventVertex createVertex(String propKey, String propValue) {
+        JsonObject properties = new JsonObject();
+        properties.addProperty(propKey, propValue);
+        GraphEventVertex vertex = new GraphEventVertex("vertex1", "v11", "pserver", properties);
+        return vertex;
+    }
+
+    private GraphEventEdge createEdge(String id, GraphEventVertex source, GraphEventVertex target, String propKey,
+            String propValue) {
+        JsonObject properties = new JsonObject();
+        properties.addProperty(propKey, propValue);
+        GraphEventEdge edge = new GraphEventEdge(id, "v11", "tosca.relationships.HostedOn", source, target, properties);
+        return edge;
+    }
+
+    @Test
+    public void computeHashForIdenticalVertexObjects() throws IOException {
+        // everything is same
+        GraphEventVertex sourceVertex1 = createVertex("prop1", "value1");
+        GraphEventVertex targetVertex1 = createVertex("prop2", "value2");
+
+        GraphEventEdge edge1 = createEdge("edge1", sourceVertex1, targetVertex1, "prop1", "value1");
+
+        GraphEventVertex sourceVertex2 = createVertex("prop1", "value1");
+        GraphEventVertex targetVertex2 = createVertex("prop2", "value2");
+
+        GraphEventEdge edge2 = createEdge("edge1", sourceVertex2, targetVertex2, "prop1", "value1");
+
+        assertThat(etagGenerator.computeHashForEdge(edge1), is(etagGenerator.computeHashForEdge(edge2)));
+    }
+
+    @Test
+    public void computeHashForVertexObjectsWithDifferentKey() throws IOException {
+        // key is different
+        GraphEventVertex sourceVertex1 = createVertex("prop1", "value1");
+        GraphEventVertex targetVertex1 = createVertex("prop2", "value2");
+
+        GraphEventEdge edge1 = createEdge("edge1", sourceVertex1, targetVertex1, "prop1", "value1");
+
+        GraphEventVertex sourceVertex2 = createVertex("prop1", "value1");
+        GraphEventVertex targetVertex2 = createVertex("prop2", "value2");
+
+        GraphEventEdge edge2 = createEdge("edge2", sourceVertex2, targetVertex2, "prop1", "value1");
+
+        assertThat(etagGenerator.computeHashForEdge(edge1), not(etagGenerator.computeHashForEdge(edge2)));
+    }
+
+    @Test
+    public void computeHashForVertexObjectsWithDifferentEdge() throws IOException {
+        // relationship is different
+        GraphEventVertex sourceVertex1 = createVertex("prop1", "value1");
+        GraphEventVertex targetVertex1 = createVertex("prop2", "value2");
+
+        GraphEventEdge edge1 = createEdge("edge1", sourceVertex1, targetVertex1, "prop1", "value1");
+
+        GraphEventVertex sourceVertex2 = createVertex("prop1", "value1");
+        GraphEventVertex targetVertex2 = createVertex("prop2", "value2");
+
+        GraphEventEdge edge2 = createEdge("edge2", sourceVertex2, targetVertex2, "prop1", "value1");
+        edge2.setType("tosca.relationships.RelatedTo");
+
+        assertThat(etagGenerator.computeHashForEdge(edge1), not(etagGenerator.computeHashForEdge(edge2)));
+    }
+
+    @Test
+    public void computeHashForEdgeObjectsWithDifferentVertexObjects() throws IOException {
+        // source/target different
+        GraphEventVertex sourceVertex1 = createVertex("prop1", "value1");
+        GraphEventVertex targetVertex1 = createVertex("prop2", "value2");
+        targetVertex1.setId("vertex2");
+
+        GraphEventEdge edge1 = createEdge("edge1", sourceVertex1, targetVertex1, "prop1", "value1");
+
+        GraphEventVertex sourceVertex2 = createVertex("prop1", "value1");
+        GraphEventVertex targetVertex2 = createVertex("prop2", "value2");
+
+        GraphEventEdge edge2 = createEdge("edge2", sourceVertex2, targetVertex2, "prop1", "value1");
+        edge2.setType("tosca.relationships.RelatedTo");
+
+        assertThat(etagGenerator.computeHashForEdge(edge1), not(etagGenerator.computeHashForEdge(edge2)));
+    }
+
+    @Test
+    public void computeHashForEdgeObjectsWithDifferentProperties() throws IOException {
+        // property different
+        GraphEventVertex sourceVertex1 = createVertex("sourceprop1", "value1");
+        GraphEventVertex targetVertex1 = createVertex("targetprop2", "value2");
+
+        GraphEventEdge edge1 = createEdge("edge1", sourceVertex1, targetVertex1, "edgeprop1", "value1");
+
+        GraphEventVertex sourceVertex2 = createVertex("sourceprop1", "value1");
+        GraphEventVertex targetVertex2 = createVertex("targetprop2", "value2");
+
+        GraphEventEdge edge2 = createEdge("edge1", sourceVertex2, targetVertex2, "edgeprop2", "value2");
+
+        assertThat(etagGenerator.computeHashForEdge(edge1), not(etagGenerator.computeHashForEdge(edge2)));
+    }
+
+    @Test
+    public void testComputeHashForIdenticalVertexObjects() throws IOException {
+        GraphEventVertex sourceVertex1 = createVertex("prop1", "value1");
+        GraphEventVertex targetVertex1 = createVertex("prop1", "value1");
+        assertThat(etagGenerator.computeHashForVertex(sourceVertex1),
+                is(etagGenerator.computeHashForVertex(targetVertex1)));
+    }
+
+    @Test
+    public void testComputeHashForVertexObjectsWithDifferentProperties() throws IOException {
+        GraphEventVertex sourceVertex1 = createVertex("prop1", "value1");
+        GraphEventVertex targetVertex1 = createVertex("prop2", "value2");
+        assertThat(etagGenerator.computeHashForVertex(sourceVertex1),
+                not(etagGenerator.computeHashForVertex(targetVertex1)));
+    }
+
+    @Test
+    public void testComputeHashForChampObjectsWithDifferentKey() throws IOException {
+        GraphEventVertex sourceVertex1 = createVertex("prop1", "value1");
+        GraphEventVertex targetVertex1 = createVertex("prop1", "value1");
+        targetVertex1.setId("vertex2");
+        assertThat(etagGenerator.computeHashForVertex(sourceVertex1),
+                not(etagGenerator.computeHashForVertex(targetVertex1)));
+    }
+
+
+}
\ No newline at end of file