From b348af8ed2c4192f88169b37bf53fa25b8a7a681 Mon Sep 17 00:00:00 2001 From: "Sotiropoulos, Ioannis (is948x)" Date: Fri, 15 Jun 2018 15:32:01 +0100 Subject: ETags on resources 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) --- src/main/java/org/onap/crud/dao/GraphDao.java | 47 ++-- .../java/org/onap/crud/dao/champ/ChampDao.java | 112 +++++---- src/main/java/org/onap/crud/event/GraphEvent.java | 14 +- .../onap/crud/event/envelope/GraphEventHeader.java | 2 +- .../org/onap/crud/parser/CrudResponseBuilder.java | 4 - .../org/onap/crud/service/AaiResourceService.java | 249 ++++++++++----------- .../crud/service/AbstractGraphDataService.java | 141 +++++++----- .../crud/service/CrudAsyncGraphDataService.java | 107 +++++++-- .../onap/crud/service/CrudGraphDataService.java | 117 +++++++--- .../org/onap/crud/service/CrudRestService.java | 61 +++-- .../java/org/onap/crud/service/EdgePayload.java | 6 +- .../java/org/onap/crud/service/VertexPayload.java | 10 +- .../org/onap/crud/util/CrudServiceConstants.java | 1 + .../java/org/onap/crud/util/CrudServiceUtil.java | 42 ++-- .../java/org/onap/crud/util/HashGenerator.java | 65 ++++++ .../org/onap/crud/util/etag/EtagGenerator.java | 92 ++++++++ 16 files changed, 683 insertions(+), 387 deletions(-) create mode 100644 src/main/java/org/onap/crud/util/HashGenerator.java create mode 100644 src/main/java/org/onap/crud/util/etag/EtagGenerator.java (limited to 'src/main/java/org/onap') diff --git a/src/main/java/org/onap/crud/dao/GraphDao.java b/src/main/java/org/onap/crud/dao/GraphDao.java index 29ea6da..fe638ce 100644 --- a/src/main/java/org/onap/crud/dao/GraphDao.java +++ b/src/main/java/org/onap/crud/dao/GraphDao.java @@ -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 queryParams) throws CrudException; + public OperationResult getVertex(String id, String type, String version, Map 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 getVertices(String type, Map filter, String version) throws CrudException; + public OperationResult getVertices(String type, Map 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 getVertices(String type, Map filter, HashSet properties, String version) throws CrudException; + public OperationResult getVertices(String type, Map filter, HashSet 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 queryParams) throws CrudException; + public OperationResult getEdge(String id, String type, Map 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 getEdges(String type, Map filter) throws CrudException; + public OperationResult getEdges(String type, Map 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 properties, String version) throws CrudException; + public OperationResult addVertex(String type, Map 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 properties, String version) throws CrudException; + public OperationResult updateVertex(String id, String type, Map 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 properties, String version) throws CrudException; + public OperationResult addEdge(String type, Vertex source, Vertex target, Map 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. diff --git a/src/main/java/org/onap/crud/dao/champ/ChampDao.java b/src/main/java/org/onap/crud/dao/champ/ChampDao.java index 344d797..c8488ba 100644 --- a/src/main/java/org/onap/crud/dao/champ/ChampDao.java +++ b/src/main/java/org/onap/crud/dao/champ/ChampDao.java @@ -20,39 +20,36 @@ */ 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 queryParams) throws CrudException { + public OperationResult getVertex(String id, String type, String version, Map 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>() { }.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 getVertices(String type, Map filter, String version) throws CrudException { + public OperationResult getVertices(String type, Map filter, String version) throws CrudException { return getVertices(type, filter, new HashSet(), version); } @Override - public List getVertices(String type, Map filter, HashSet properties, String version) throws CrudException { + public OperationResult getVertices(String type, Map filter, HashSet properties, String version) throws CrudException { filter.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); List 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 queryParams) throws CrudException { + public OperationResult getEdge(String id, String type, Map 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 getEdges(String type, Map filter) throws CrudException { + public OperationResult getEdges(String type, Map 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>() { - }.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 properties, String version) throws CrudException { + public OperationResult addVertex(String type, Map 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 properties, String version) throws CrudException { + public OperationResult updateVertex(String id, String type, Map 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 properties, String version) throws CrudException { + public OperationResult addEdge(String type, Vertex source, Vertex target, Map 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()); - Vertex dbTarget = getVertex(target.getId().get(), target.getType(), version, new HashMap()); + OperationResult dbSourceOpResult = getVertex(source.getId().get(), source.getType(), version, new HashMap()); + Vertex dbSource = Vertex.fromJson(dbSourceOpResult.getResult(), version); + OperationResult dbTargetOpResult = getVertex(target.getId().get(), target.getType(), version, new HashMap()); + 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> createHeader() { Map> 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) diff --git a/src/main/java/org/onap/crud/event/GraphEvent.java b/src/main/java/org/onap/crud/event/GraphEvent.java index 63b84fd..958c227 100644 --- a/src/main/java/org/onap/crud/event/GraphEvent.java +++ b/src/main/java/org/onap/crud/event/GraphEvent.java @@ -20,14 +20,13 @@ */ 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) { diff --git a/src/main/java/org/onap/crud/event/envelope/GraphEventHeader.java b/src/main/java/org/onap/crud/event/envelope/GraphEventHeader.java index 4f914cf..81613dd 100644 --- a/src/main/java/org/onap/crud/event/envelope/GraphEventHeader.java +++ b/src/main/java/org/onap/crud/event/envelope/GraphEventHeader.java @@ -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) diff --git a/src/main/java/org/onap/crud/parser/CrudResponseBuilder.java b/src/main/java/org/onap/crud/parser/CrudResponseBuilder.java index 0c66d81..0a81884 100644 --- a/src/main/java/org/onap/crud/parser/CrudResponseBuilder.java +++ b/src/main/java/org/onap/crud/parser/CrudResponseBuilder.java @@ -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; diff --git a/src/main/java/org/onap/crud/service/AaiResourceService.java b/src/main/java/org/onap/crud/service/AaiResourceService.java index c9a5805..afabe7e 100644 --- a/src/main/java/org/onap/crud/service/AaiResourceService.java +++ b/src/main/java/org/onap/crud/service/AaiResourceService.java @@ -26,7 +26,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; - import javax.security.auth.x500.X500Principal; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; @@ -37,20 +36,21 @@ 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.UriInfo; import javax.ws.rs.core.Response.Status; - +import javax.ws.rs.core.UriInfo; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; import org.onap.aai.exceptions.AAIException; import org.onap.aai.serialization.db.EdgeProperty; import org.onap.aai.serialization.db.EdgeRule; import org.onap.aai.serialization.db.EdgeRules; import org.onap.aai.serialization.db.EdgeType; import org.onap.aaiauth.auth.Auth; -import org.onap.aai.cl.api.Logger; -import org.onap.aai.cl.eelf.LoggerFactory; import org.onap.crud.exception.CrudException; import org.onap.crud.logging.CrudServiceMsgs; import org.onap.crud.logging.LoggingUtil; @@ -59,7 +59,6 @@ import org.onap.crud.util.CrudServiceConstants; import org.onap.schema.EdgeRulesLoader; import org.onap.schema.RelationshipSchemaValidator; import org.slf4j.MDC; - import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonPrimitive; @@ -74,78 +73,78 @@ public class AaiResourceService { private String mediaType = MediaType.APPLICATION_JSON; public static final String HTTP_PATCH_METHOD_OVERRIDE = "X-HTTP-Method-Override"; - + private Auth auth; AbstractGraphDataService graphDataService; Gson gson = new Gson(); - + private Logger logger = LoggerFactory.getInstance().getLogger(AaiResourceService.class.getName()); private Logger auditLogger = LoggerFactory.getInstance().getAuditLogger(AaiResourceService.class.getName()); - + public AaiResourceService() {} - + /** * Creates a new instance of the AaiResourceService. - * + * * @param crudGraphDataService - Service used for interacting with the graph. - * + * * @throws Exception */ public AaiResourceService(AbstractGraphDataService graphDataService) throws Exception { this.graphDataService = graphDataService; this.auth = new Auth(CrudServiceConstants.CRD_AUTH_FILE); } - + /** * Perform any one-time initialization required when starting the service. */ public void startup() { - + if(logger.isDebugEnabled()) { logger.debug("AaiResourceService started!"); } } - - + + /** * Creates a new relationship in the graph, automatically populating the edge * properties based on the A&AI edge rules. - * + * * @param content - Json structure describing the relationship to create. * @param type - Relationship type supplied as a URI parameter. * @param uri - Http request uri * @param headers - Http request headers * @param uriInfo - Http URI info field * @param req - Http request structure. - * + * * @return - Standard HTTP response. */ @POST @Path("/relationships/{type}/") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) - public Response createRelationship(String content, - @PathParam("type") String type, + public Response createRelationship(String content, + @PathParam("type") String type, @PathParam("uri") @Encoded String uri, - @Context HttpHeaders headers, + @Context HttpHeaders headers, @Context UriInfo uriInfo, @Context HttpServletRequest req) { - + LoggingUtil.initMdcContext(req, headers); if(logger.isDebugEnabled()) { logger.debug("Incoming request..." + content); } - + Response response = null; if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { - + try { - + // Extract the edge payload from the request. - EdgePayload payload = EdgePayload.fromJson(content); - + EdgePayload payload = EdgePayload.fromJson(content); + // Do some basic validation on the payload. if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); @@ -156,50 +155,50 @@ public class AaiResourceService { if (payload.getType() != null && !payload.getType().equals(type)) { throw new CrudException("Edge Type mismatch", Status.BAD_REQUEST); } - + // Apply the edge rules to our edge. payload = applyEdgeRulesToPayload(payload); - + if(logger.isDebugEnabled()) { logger.debug("Creating AAI edge using version " + EdgeRulesLoader.getLatestSchemaVersion() ); } - + // Now, create our edge in the graph store. - String result = graphDataService.addEdge(EdgeRulesLoader.getLatestSchemaVersion(), type, payload); - response = Response.status(Status.CREATED).entity(result).type(mediaType).build(); - + ImmutablePair result = graphDataService.addEdge(EdgeRulesLoader.getLatestSchemaVersion(), type, payload); + response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build(); + } catch (CrudException e) { response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); - } + } } - + LoggingUtil.logRestRequest(logger, auditLogger, req, response); return response; } - - + + /** * Creates a new relationship in the graph, automatically populating the edge * properties based on the A&AI edge rules. - * + * * @param content - Json structure describing the relationship to create. * @param uri - Http request uri * @param headers - Http request headers * @param uriInfo - Http URI info field * @param req - Http request structure. - * + * * @return - Standard HTTP response. - * + * */ @POST @Path("/relationships/") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) - public Response createRelationship(String content, - @PathParam("uri") @Encoded String uri, + public Response createRelationship(String content, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, - @Context UriInfo uriInfo, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { LoggingUtil.initMdcContext(req, headers); @@ -210,10 +209,10 @@ public class AaiResourceService { if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { try { - + // Extract the edge payload from the request. EdgePayload payload = EdgePayload.fromJson(content); - + // Do some basic validation on the payload. if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); @@ -224,14 +223,14 @@ public class AaiResourceService { if (payload.getType() == null || payload.getType().isEmpty()) { throw new CrudException("Missing Edge Type ", Status.BAD_REQUEST); } - + // Apply the edge rules to our edge. payload = applyEdgeRulesToPayload(payload); - + // Now, create our edge in the graph store. - String result = graphDataService.addEdge(EdgeRulesLoader.getLatestSchemaVersion(), payload.getType(), payload); - response = Response.status(Status.CREATED).entity(result).type(mediaType).build(); - + ImmutablePair result = graphDataService.addEdge(EdgeRulesLoader.getLatestSchemaVersion(), payload.getType(), payload); + response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build(); + } catch (CrudException ce) { response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); } catch (Exception e) { @@ -246,17 +245,17 @@ public class AaiResourceService { return response; } - - + + /** * Upserts a relationship into the graph, automatically populating the edge properties * based on the A&AI edge rules. The behaviour is as follows: *

- *

  • If no relationship with the supplied identifier already exists, then a new relationship + *
  • If no relationship with the supplied identifier already exists, then a new relationship * is created with that id.
    - *
  • If a relationship with the supplied id DOES exist, then it is replaced with the supplied + *
  • If a relationship with the supplied id DOES exist, then it is replaced with the supplied * content. - * + * * @param content - Json structure describing the relationship to create. * @param type - Relationship type supplied as a URI parameter. * @param id - Edge identifier. @@ -264,19 +263,19 @@ public class AaiResourceService { * @param headers - Http request headers * @param uriInfo - Http URI info field * @param req - Http request structure. - * + * * @return - Standard HTTP response. */ @PUT @Path("/relationships/{type}/{id}") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) - public Response upsertEdge(String content, - @PathParam("type") String type, + public Response upsertEdge(String content, + @PathParam("type") String type, @PathParam("id") String id, - @PathParam("uri") @Encoded String uri, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, - @Context UriInfo uriInfo, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { LoggingUtil.initMdcContext(req, headers); @@ -284,12 +283,12 @@ public class AaiResourceService { Response response = null; if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { - + try { - + // Extract the edge payload from the request. EdgePayload payload = EdgePayload.fromJson(content); - + // Do some basic validation on the payload. if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); @@ -297,47 +296,45 @@ public class AaiResourceService { if (payload.getId() != null && !payload.getId().equals(id)) { throw new CrudException("ID Mismatch", Status.BAD_REQUEST); } - + // Apply the edge rules to our edge. payload = applyEdgeRulesToPayload(payload); - - String result; + ImmutablePair result; if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE).equalsIgnoreCase("PATCH")) { result = graphDataService.patchEdge(EdgeRulesLoader.getLatestSchemaVersion(), id, type, payload); + response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build(); } else { - result = graphDataService.updateEdge(EdgeRulesLoader.getLatestSchemaVersion(), 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(); - } catch (CrudException ce) { response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); } catch (Exception e) { response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); } - + } else { - + response = Response.status(Status.FORBIDDEN).entity(content) .type(MediaType.APPLICATION_JSON).build(); } - + LoggingUtil.logRestRequest(logger, auditLogger, req, response); return response; } - - + + /** - * Retrieves the properties defined in the edge rules for a relationship between the + * Retrieves the properties defined in the edge rules for a relationship between the * supplied vertex types. - * + * * @param sourceVertexType - Type of source vertex for the relationship. * @param targetVertexType - Type of target vertex for the relationship. - * + * * @return - The defined properties for the relationship type. - * + * * @throws CrudException */ private Map getEdgeRuleProperties(String sourceVertexType, String targetVertexType) throws CrudException { @@ -345,119 +342,119 @@ public class AaiResourceService { if(logger.isDebugEnabled()) { logger.debug("Lookup db edge rules for " + sourceVertexType + " -> " + targetVertexType); } - + EdgeRules rules = EdgeRules.getInstance(); EdgeRule rule; try { - + if(logger.isDebugEnabled()) { logger.debug("Lookup by edge type TREE"); } - + // We have no way of knowing in advance whether our relationship is considered to // be a tree or cousing relationship, so try looking it up as a tree type first. rule = rules.getEdgeRule(EdgeType.TREE, sourceVertexType, targetVertexType); - + } catch (AAIException e) { try { - + if(logger.isDebugEnabled()) { logger.debug("Lookup by edge type COUSIN"); } - + // If we are here, then our lookup by 'tree' type failed, so try looking it up // as a 'cousin' relationship. rule = rules.getEdgeRule(EdgeType.COUSIN, sourceVertexType, targetVertexType); - + } catch (AAIException e1) { - + // If we're here then we failed to find edge rules for this relationship. Time to // give up... throw new CrudException("No edge rules for " + sourceVertexType + " -> " + targetVertexType, Status.NOT_FOUND); } } catch (Exception e) { - - throw new CrudException("General failure getting edge rule properties - " + + + throw new CrudException("General failure getting edge rule properties - " + e.getMessage(), Status.INTERNAL_SERVER_ERROR); } - + return rule.getEdgeProperties(); } - - + + /** * This method takes an inbound edge request payload, looks up the edge rules for the * sort of relationship defined in the payload, and automatically applies the defined * edge properties to it. - * + * * @param payload - The original edge request payload - * + * * @return - An updated edge request payload, with the properties defined in the edge * rules automatically populated. - * + * * @throws CrudException */ public EdgePayload applyEdgeRulesToPayload(EdgePayload payload) throws CrudException { - + // Extract the types for both the source and target vertices. String srcType = RelationshipSchemaValidator.vertexTypeFromUri(payload.getSource()); String tgtType = RelationshipSchemaValidator.vertexTypeFromUri(payload.getTarget()); // Now, get the default properties for this edge based on the edge rules definition... Map props = getEdgeRuleProperties(srcType, tgtType); - + // ...and merge them with any custom properties provided in the request. JsonElement mergedProperties = mergeProperties(payload.getProperties(), props); payload.setProperties(mergedProperties); - - + + if(logger.isDebugEnabled()) { logger.debug("Edge properties after applying rules for '" + srcType + " -> " + tgtType + "': " + mergedProperties); } - + return payload; } - - + + /** * Given a set of edge properties extracted from an edge request payload and a set of properties * taken from the db edge rules, this method merges them into one set of properties. *

    * If the client has attempted to override the defined value for a property in the db edge rules * then the request will be rejected as invalid. - * + * * @param propertiesFromRequest - Set of properties from the edge request. * @param propertyDefaults - Set of properties from the db edge rules. - * + * * @return - A merged set of properties. - * + * * @throws CrudException */ public JsonElement mergeProperties(JsonElement propertiesFromRequest, Map propertyDefaults) throws CrudException { - + // Convert the properties from the edge payload into something we can // manipulate. Set> properties = new HashSet>(); properties.addAll(propertiesFromRequest.getAsJsonObject().entrySet()); - + Set propertyKeys = new HashSet(); for(Map.Entry property : properties) { propertyKeys.add(property.getKey()); } - + // Now, merge in the properties specified in the Db Edge Rules. for(EdgeProperty defProperty : propertyDefaults.keySet()) { - + // If the edge rules property was explicitly specified by the // client then we will reject the request... if(!propertyKeys.contains(defProperty.toString())) { properties.add(new AbstractMap.SimpleEntry(defProperty.toString(), - (JsonElement)(new JsonPrimitive(propertyDefaults.get(defProperty))))); - + (new JsonPrimitive(propertyDefaults.get(defProperty))))); + } else { - throw new CrudException("Property " + defProperty + " defined in db edge rules can not be overriden by the client.", + throw new CrudException("Property " + defProperty + " defined in db edge rules can not be overriden by the client.", Status.BAD_REQUEST); - } + } } Object[] propArray = properties.toArray(); @@ -465,7 +462,7 @@ public class AaiResourceService { sb.append("{"); boolean first=true; for(int i=0; i entry = (Entry) propArray[i]; if(!first) { sb.append(","); @@ -474,7 +471,7 @@ public class AaiResourceService { first=false; } sb.append("}"); - + // We're done. Return the result as a JsonElement. return gson.fromJson(sb.toString(), JsonElement.class); } @@ -482,45 +479,45 @@ public class AaiResourceService { /** * Invokes authentication validation on an incoming HTTP request. - * + * * @param req - The HTTP request. * @param uri - HTTP URI * @param content - Payload of the HTTP request. * @param action - What HTTP action is being performed (GET/PUT/POST/PATCH/DELETE) * @param authPolicyFunctionName - Policy function being invoked. - * + * * @return true - if the request passes validation, * false - otherwise. */ - protected boolean validateRequest(HttpServletRequest req, - String uri, + protected boolean validateRequest(HttpServletRequest req, + String uri, String content, - Action action, + Action action, String authPolicyFunctionName) { try { String cipherSuite = (String) req.getAttribute("javax.servlet.request.cipher_suite"); String authUser = null; if (cipherSuite != null) { - + X509Certificate[] certChain = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate"); X509Certificate clientCert = certChain[0]; X500Principal subjectDn = clientCert.getSubjectX500Principal(); authUser = subjectDn.toString(); } - + return this.auth.validateRequest(authUser.toLowerCase(), action.toString() + ":" + authPolicyFunctionName); - + } catch (Exception e) { logResult(action, uri, e); return false; } } - + protected void logResult(Action op, String uri, Exception e) { - logger.error(CrudServiceMsgs.EXCEPTION_DURING_METHOD_CALL, - op.toString(), - uri, + logger.error(CrudServiceMsgs.EXCEPTION_DURING_METHOD_CALL, + op.toString(), + uri, e.getStackTrace().toString()); // Clear the MDC context so that no other transaction inadvertently diff --git a/src/main/java/org/onap/crud/service/AbstractGraphDataService.java b/src/main/java/org/onap/crud/service/AbstractGraphDataService.java index 9c7e0d4..7c1168e 100644 --- a/src/main/java/org/onap/crud/service/AbstractGraphDataService.java +++ b/src/main/java/org/onap/crud/service/AbstractGraphDataService.java @@ -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 queryParams) throws CrudException { + public ImmutablePair getEdge(String version, String id, String type, Map 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 filter) throws CrudException { + + public ImmutablePair getEdges(String version, String type, Map 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 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 items = champGson.fromJson(operationResult.getResult(), new TypeToken>() { + }.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 queryParams) throws CrudException { + + public ImmutablePair getVertex(String version, String id, String type, Map 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 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 filter, HashSet properties) throws CrudException { + public ImmutablePair getVertices(String version, String type, Map filter, HashSet properties) throws CrudException { type = OxmModelValidator.resolveCollectionType(version, type); - List 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 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 vertices = new HashMap(); - HashMap edges = new HashMap(); - - String txId = dao.openTransaction(); - + HashMap vertices = new HashMap<>(); + HashMap 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> entries = new ArrayList>( @@ -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> entries = new ArrayList>( @@ -130,21 +148,21 @@ public abstract class AbstractGraphDataService { Map.Entry opr = entries.get(0); Map.Entry 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()); - Vertex validatedVertex = OxmModelValidator.validateIncomingPatchPayload(vertexPayload.getId(), + + OperationResult existingVertexOpResult = dao.getVertex(vertexPayload.getId(), OxmModelValidator.resolveCollectionType(version, vertexPayload.getType()), version, new HashMap()); + 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> entries = new ArrayList>( 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 addVertex(String version, String type, VertexPayload payload) + throws CrudException; + public abstract ImmutablePair updateVertex(String version, String id, String type, + VertexPayload payload) throws CrudException; + public abstract ImmutablePair 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 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 updateEdge(String version, String id, String type, + EdgePayload payload) throws CrudException; + public abstract ImmutablePair 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; - + } diff --git a/src/main/java/org/onap/crud/service/CrudAsyncGraphDataService.java b/src/main/java/org/onap/crud/service/CrudAsyncGraphDataService.java index 5d37acb..6b447a1 100644 --- a/src/main/java/org/onap/crud/service/CrudAsyncGraphDataService.java +++ b/src/main/java/org/onap/crud/service/CrudAsyncGraphDataService.java @@ -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 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, responsePayload); } @Override - public String addEdge(String version, String type, EdgePayload payload) throws CrudException { + public ImmutablePair 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, responsePayload); } @Override - public String updateVertex(String version, String id, String type, VertexPayload payload) throws CrudException { + public ImmutablePair 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, 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 patchVertex(String version, String id, String type, VertexPayload payload) + throws CrudException { + OperationResult existingVertexOpResult = dao.getVertex(id, OxmModelValidator.resolveCollectionType(version, type), version, new HashMap()); + 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, 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()); + public ImmutablePair updateEdge(String version, String id, String type, EdgePayload payload) + throws CrudException { + OperationResult operationResult = dao.getEdge(id, type, new HashMap()); + 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, responsePayload); } @Override - public String patchEdge(String version, String id, String type, EdgePayload payload) throws CrudException { - Edge edge = dao.getEdge(id, type, new HashMap()); + public ImmutablePair patchEdge(String version, String id, String type, EdgePayload payload) + throws CrudException { + OperationResult operationResult = dao.getEdge(id, type, new HashMap()); + 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, responsePayload); } @PreDestroy @@ -349,4 +420,4 @@ public class CrudAsyncGraphDataService extends AbstractGraphDataService { responseHandler.handleBulkEventResponse(event, response); return response.getBody(); } -} +} \ No newline at end of file diff --git a/src/main/java/org/onap/crud/service/CrudGraphDataService.java b/src/main/java/org/onap/crud/service/CrudGraphDataService.java index 5a2710d..5b1c2dd 100644 --- a/src/main/java/org/onap/crud/service/CrudGraphDataService.java +++ b/src/main/java/org/onap/crud/service/CrudGraphDataService.java @@ -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 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 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, payload); } - public String addEdge(String version, String type, EdgePayload payload) throws CrudException { + @Override + public ImmutablePair 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 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, payload); } - public String updateVertex(String version, String id, String type, VertexPayload payload) throws CrudException { + @Override + public ImmutablePair 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 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, 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()); + @Override + public ImmutablePair patchVertex(String version, String id, String type, VertexPayload payload) + throws CrudException { + OperationResult existingVertexOpResult = dao.getVertex(id, OxmModelValidator.resolveCollectionType(version, type), version, new HashMap()); + 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()); - Edge validatedEdge = RelationshipSchemaValidator.validateIncomingUpdatePayload(edge, version, payload); + @Override + public ImmutablePair 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 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, 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()); + 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()); - Edge patchedEdge = RelationshipSchemaValidator.validateIncomingPatchPayload(edge, version, payload); - return updateEdge(version, patchedEdge); + @Override + public ImmutablePair patchEdge(String version, String id, String type, EdgePayload payload) + throws CrudException { + OperationResult operationResult = dao.getEdge(id, type, new HashMap()); + 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); diff --git a/src/main/java/org/onap/crud/service/CrudRestService.java b/src/main/java/org/onap/crud/service/CrudRestService.java index 2cbb87c..583fee6 100644 --- a/src/main/java/org/onap/crud/service/CrudRestService.java +++ b/src/main/java/org/onap/crud/service/CrudRestService.java @@ -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 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 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 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 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 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 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 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 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 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 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 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 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(); } diff --git a/src/main/java/org/onap/crud/service/EdgePayload.java b/src/main/java/org/onap/crud/service/EdgePayload.java index 630ec02..a670b54 100644 --- a/src/main/java/org/onap/crud/service/EdgePayload.java +++ b/src/main/java/org/onap/crud/service/EdgePayload.java @@ -20,14 +20,12 @@ */ 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; diff --git a/src/main/java/org/onap/crud/service/VertexPayload.java b/src/main/java/org/onap/crud/service/VertexPayload.java index 594dc1a..172d03b 100644 --- a/src/main/java/org/onap/crud/service/VertexPayload.java +++ b/src/main/java/org/onap/crud/service/VertexPayload.java @@ -20,15 +20,13 @@ */ 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 { diff --git a/src/main/java/org/onap/crud/util/CrudServiceConstants.java b/src/main/java/org/onap/crud/util/CrudServiceConstants.java index 3a02852..ae5b464 100644 --- a/src/main/java/org/onap/crud/util/CrudServiceConstants.java +++ b/src/main/java/org/onap/crud/util/CrudServiceConstants.java @@ -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"; } diff --git a/src/main/java/org/onap/crud/util/CrudServiceUtil.java b/src/main/java/org/onap/crud/util/CrudServiceUtil.java index 6c251bc..6b5cdcd 100644 --- a/src/main/java/org/onap/crud/util/CrudServiceUtil.java +++ b/src/main/java/org/onap/crud/util/CrudServiceUtil.java @@ -20,6 +20,15 @@ */ 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(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(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 entry = (Entry) 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 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 index 0000000..02558fa --- /dev/null +++ b/src/main/java/org/onap/crud/util/HashGenerator.java @@ -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 index 0000000..b288b78 --- /dev/null +++ b/src/main/java/org/onap/crud/util/etag/EtagGenerator.java @@ -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 convertPropertiesToMap(JsonElement properties) { + Map propertiesMap = new HashMap<>(); + if (null != properties) { + JsonObject propsObject = properties.getAsJsonObject(); + for (Entry props : propsObject.entrySet()) { + String key = props.getKey(); + String value = props.getValue().getAsString(); + propertiesMap.put(key, value); + } + } + return filterAndSortProperties(propertiesMap); + } + + private Map filterAndSortProperties(Map 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 -- cgit 1.2.3-korg