From be2eab9395a787ca4b13447f9d2c382d12f5dcd5 Mon Sep 17 00:00:00 2001 From: "Bansal, Nitin (nb121v)" Date: Thu, 16 Nov 2017 14:56:59 -0500 Subject: Add bulk API to gizmo Add bulk API to gizmo IssueID: AAI-481 Change-Id: Iff9df1a8fdc73c87d726da7294c2eb9f471080f1 Signed-off-by: Bansal, Nitin (nb121v) --- .../org/openecomp/crud/service/BulkPayload.java | 128 ++++++++++++++ .../crud/service/CrudGraphDataService.java | 143 +++++++++++++++- .../openecomp/crud/service/CrudRestService.java | 190 ++++++++++++++++----- 3 files changed, 414 insertions(+), 47 deletions(-) create mode 100644 src/main/java/org/openecomp/crud/service/BulkPayload.java (limited to 'src/main/java/org/openecomp/crud/service') diff --git a/src/main/java/org/openecomp/crud/service/BulkPayload.java b/src/main/java/org/openecomp/crud/service/BulkPayload.java new file mode 100644 index 0000000..28cf420 --- /dev/null +++ b/src/main/java/org/openecomp/crud/service/BulkPayload.java @@ -0,0 +1,128 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.service; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import org.openecomp.crud.exception.CrudException; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.core.Response.Status; + +public class BulkPayload { + public enum OperationType { + CREATE, UPDATE, DELETE + } + + + + private List objects = new ArrayList(); + private List relationships = new ArrayList(); + + private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + public String toJson() { + return gson.toJson(this); + } + + public static BulkPayload fromJson(String payload) throws CrudException { + try { + if (payload == null || payload.isEmpty()) { + throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST); + } + return gson.fromJson(payload, BulkPayload.class); + } catch (Exception ex) { + throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST); + } + } + + public List getObjects() { + return objects; + } + + public void setObjects(List objects) { + this.objects = objects; + } + + public List getRelationships() { + return relationships; + } + + public void setRelationships(List relationships) { + this.relationships = relationships; + } + + @Override + public String toString() { + return "BulkPayload [objects=" + objects + ", relationships=" + relationships + "]"; + } + + public static void main(String[] args) throws Exception { + BulkPayload p = new BulkPayload(); + JsonObject root = new JsonObject(); + JsonArray vertices = new JsonArray(); + JsonObject v1 = new JsonObject(); + JsonObject v2 = new JsonObject(); + JsonObject prop = new JsonObject(); + + prop.addProperty("p1","value1"); + prop.addProperty("p2","value2"); + v1.add("v1", prop); + v2.add("v2",prop); + + vertices.add(v1); + vertices.add(v2); + + + root.add("objects", vertices); + + String s = "{\"objects\":[{\"v1\":{\"p1\":\"value1\",\"p2\":\"value2\"}},{\"v2\":{\"p1\":\"value1\",\"p2\":\"value2\"}}]}"; + + p = BulkPayload.fromJson(s); + + List po = p.getObjects(); + List ids = new ArrayList(); + for (JsonElement e : po){ + Set> entries = e.getAsJsonObject().entrySet(); + + for (Map.Entry entry : entries) { + ids.add(entry.getKey()); + } + } + + + System.out.println("root: " + root.toString()); + System.out.println("payload ids: " + ids.toString()); + + } + +} \ No newline at end of file diff --git a/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java b/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java index f38fe0f..c6b6a48 100644 --- a/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java +++ b/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java @@ -23,11 +23,19 @@ */ package org.openecomp.crud.service; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.Response.Status; + import org.onap.aaiutils.oxm.OxmModelLoader; import org.openecomp.aai.champcore.ChampGraph; import org.openecomp.crud.dao.GraphDao; import org.openecomp.crud.dao.champ.ChampDao; import org.openecomp.crud.entity.Edge; + import org.openecomp.crud.entity.Vertex; import org.openecomp.crud.exception.CrudException; import org.openecomp.crud.parser.CrudResponseBuilder; @@ -35,32 +43,149 @@ import org.openecomp.schema.OxmModelValidator; import org.openecomp.schema.RelationshipSchemaLoader; import org.openecomp.schema.RelationshipSchemaValidator; -import java.util.List; -import java.util.Map; +import com.google.gson.JsonElement; public class CrudGraphDataService { private GraphDao dao; public CrudGraphDataService(ChampGraph graphImpl) throws CrudException { + this.dao = new ChampDao(graphImpl); + + loadModels(); + } - this.dao = new ChampDao(graphImpl); - //load the schemas - try { + public CrudGraphDataService(GraphDao dao) throws CrudException { + this.dao = dao; + + loadModels(); + } + + private void loadModels() throws CrudException { + //load the schemas + try { OxmModelLoader.loadModels(); } catch (Exception e) { throw new CrudException(e); } - RelationshipSchemaLoader.loadModels(); - } - + RelationshipSchemaLoader.loadModels(); + } public String addVertex(String version, String type, VertexPayload payload) throws CrudException { Vertex vertex = OxmModelValidator.validateIncomingUpsertPayload(null, version, type, payload.getProperties()); return addVertex(version, vertex); } + + public String addBulk(String version, BulkPayload payload) throws CrudException { + HashMap vertices = new HashMap(); + HashMap edges = new HashMap(); + String txId = dao.openTransaction(); + try { + // Handle vertices + for (JsonElement v : payload.getObjects()) { + List> entries = new ArrayList>(v.getAsJsonObject().entrySet()); + + if (entries.size() != 2) { + throw new CrudException("", Status.BAD_REQUEST); + } + Map.Entry opr = entries.get(0); + Map.Entry item = entries.get(1); + + VertexPayload vertexPayload = VertexPayload.fromJson(item.getValue().getAsJsonObject().toString()); + + if (opr.getValue().getAsString().equalsIgnoreCase("add") || opr.getValue().getAsString().equalsIgnoreCase("modify")) { + Vertex validatedVertex; + Vertex persistedVertex; + if (opr.getValue().getAsString().equalsIgnoreCase("add")) { + validatedVertex = OxmModelValidator.validateIncomingUpsertPayload(null, version, vertexPayload.getType(), + vertexPayload.getProperties()); + // Call champDAO to add the vertex + persistedVertex = dao.addVertex(validatedVertex.getType(), validatedVertex.getProperties(), txId); + } else { + validatedVertex = OxmModelValidator.validateIncomingUpsertPayload(vertexPayload.getId(), version, vertexPayload.getType(), + vertexPayload.getProperties()); + // Call champDAO to update the vertex + persistedVertex = dao.updateVertex(vertexPayload.getId(), validatedVertex.getType(), validatedVertex.getProperties(), txId); + } + + Vertex outgoingVertex = OxmModelValidator.validateOutgoingPayload(version, persistedVertex); + + vertices.put(item.getKey(), outgoingVertex); + + } else if (opr.getValue().getAsString().equalsIgnoreCase("delete")) { + dao.deleteVertex(vertexPayload.getId(), OxmModelValidator.resolveCollectionType(version, vertexPayload.getType()), txId); + } + + } + // Handle Edges + for (JsonElement v : payload.getRelationships()) { + List> entries = new ArrayList>(v.getAsJsonObject().entrySet()); + + if (entries.size() != 2) { + throw new CrudException("", Status.BAD_REQUEST); + } + Map.Entry opr = entries.get(0); + Map.Entry item = entries.get(1); + + EdgePayload edgePayload = EdgePayload.fromJson(item.getValue().getAsJsonObject().toString()); + + if (opr.getValue().getAsString().equalsIgnoreCase("add") || opr.getValue().getAsString().equalsIgnoreCase("modify")) { + Edge validatedEdge; + Edge persistedEdge; + if (opr.getValue().getAsString().equalsIgnoreCase("add")) { + // Fix the source/detination + if (edgePayload.getSource().startsWith("$")) { + Vertex source = vertices.get(edgePayload.getSource().substring(1)); + if (source == null) { + throw new CrudException("Not able to find vertex: " + edgePayload.getSource().substring(1), Status.INTERNAL_SERVER_ERROR); + } + edgePayload.setSource("services/inventory/" + version + "/" + source.getType() + "/" + source.getId().get()); + } + if (edgePayload.getTarget().startsWith("$")) { + Vertex target = vertices.get(edgePayload.getTarget().substring(1)); + if (target == null) { + throw new CrudException("Not able to find vertex: " + edgePayload.getTarget().substring(1), Status.INTERNAL_SERVER_ERROR); + } + edgePayload.setTarget("services/inventory/" + version + "/" + target.getType() + "/" + target.getId().get()); + } + validatedEdge = RelationshipSchemaValidator.validateIncomingAddPayload(version, edgePayload.getType(), edgePayload); + persistedEdge = dao.addEdge(validatedEdge.getType(), validatedEdge.getSource(), validatedEdge.getTarget(), + validatedEdge.getProperties(), txId); + } else { + Edge edge = dao.getEdge(edgePayload.getId(), edgePayload.getType(), txId); + validatedEdge = RelationshipSchemaValidator.validateIncomingUpdatePayload(edge, version, edgePayload); + persistedEdge = dao.updateEdge(edge, txId); + } + + Edge outgoingEdge = RelationshipSchemaValidator.validateOutgoingPayload(version, persistedEdge); + + edges.put(item.getKey(), outgoingEdge); + + } else if (opr.getValue().getAsString().equalsIgnoreCase("delete")) { + RelationshipSchemaValidator.validateType(version, edgePayload.getType()); + dao.deleteEdge(edgePayload.getId(), edgePayload.getType(), txId); + } + + } + // close champ TX + dao.commitTransaction(txId); + } catch (CrudException ex) { + dao.rollbackTransaction(txId); + throw ex; + } catch (Exception ex) { + dao.rollbackTransaction(txId); + throw ex; + } finally { + if (dao.transactionExists(txId)) { + dao.rollbackTransaction(txId); + } + } + + + return CrudResponseBuilder.buildUpsertBulkResponse(vertices, edges, version, payload); + } private String addVertex(String version, Vertex vertex) throws CrudException { Vertex addedVertex = dao.addVertex(vertex.getType(), vertex.getProperties()); @@ -182,5 +307,7 @@ public class CrudGraphDataService { type, filter)); return CrudResponseBuilder.buildGetVerticesResponse(items, version); } + + } diff --git a/src/main/java/org/openecomp/crud/service/CrudRestService.java b/src/main/java/org/openecomp/crud/service/CrudRestService.java index bef7b04..8a9f1c0 100644 --- a/src/main/java/org/openecomp/crud/service/CrudRestService.java +++ b/src/main/java/org/openecomp/crud/service/CrudRestService.java @@ -23,20 +23,13 @@ */ package org.openecomp.crud.service; -import org.apache.cxf.jaxrs.ext.PATCH; -import org.openecomp.auth.Auth; -import org.openecomp.cl.api.Logger; -import org.openecomp.cl.eelf.LoggerFactory; -import org.openecomp.crud.exception.CrudException; -import org.openecomp.crud.logging.CrudServiceMsgs; -import org.openecomp.crud.logging.LoggingUtil; -import org.openecomp.crud.util.CrudServiceConstants; -import org.slf4j.MDC; - import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; + import javax.security.auth.x500.X500Principal; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; @@ -55,6 +48,18 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; +import org.apache.cxf.jaxrs.ext.PATCH; +import org.openecomp.auth.Auth; +import org.openecomp.cl.api.Logger; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.logging.CrudServiceMsgs; +import org.openecomp.crud.logging.LoggingUtil; +import org.openecomp.crud.util.CrudServiceConstants; +import org.slf4j.MDC; + +import com.google.gson.JsonElement; + public class CrudRestService { private CrudGraphDataService crudGraphDataService; @@ -221,21 +226,6 @@ public class CrudRestService { return response; } - @PUT - @Path("/relationships/{version}/{type}/") - @Consumes({MediaType.APPLICATION_JSON}) - @Produces({MediaType.APPLICATION_JSON}) - public Response updateEdgeWithoutId(String content, @Context HttpHeaders headers, @Context HttpServletRequest req) { - - LoggingUtil.initMdcContext(req, headers); - - logger.debug("Incoming request..." + content); - Response response = Response.status (Status.BAD_REQUEST).entity ("Cannot Update Edge Without Specifying Id in URL").build(); - - LoggingUtil.logRestRequest(logger, auditLogger, req, response); - return response; - } - @PUT @Path("/relationships/{version}/{type}/{id}") @Consumes({MediaType.APPLICATION_JSON}) @@ -460,6 +450,142 @@ public class CrudRestService { .type(MediaType.APPLICATION_JSON).build(); } + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + private void validateBulkPayload(BulkPayload payload) throws CrudException { + List vertices = new ArrayList(); + List edges = new ArrayList(); + + for (JsonElement v : payload.getObjects()) { + List> entries = new ArrayList>(v.getAsJsonObject().entrySet()); + + if (entries.size() != 2) { + throw new CrudException("", Status.BAD_REQUEST); + } + Map.Entry opr = entries.get(0); + Map.Entry item = entries.get(1); + + if (vertices.contains(item.getKey())) { + throw new CrudException("duplicate vertex in payload: " + item.getKey(), Status.BAD_REQUEST); + } + VertexPayload vertexPayload = VertexPayload.fromJson(item.getValue().getAsJsonObject().toString()); + if (vertexPayload.getType() == null) { + throw new CrudException("Vertex Type cannot be null for: " + item.getKey(), Status.BAD_REQUEST); + } + + if (!opr.getKey().equalsIgnoreCase("operation")) { + throw new CrudException("operation missing in item: " + item.getKey(), Status.BAD_REQUEST); + } + + if (!opr.getValue().getAsString().equalsIgnoreCase("add") && !opr.getValue().getAsString().equalsIgnoreCase("modify") + && !opr.getValue().getAsString().equalsIgnoreCase("delete")) { + throw new CrudException("Invalid operation at item: " + item.getKey(), Status.BAD_REQUEST); + } + // check if ID is populate for modify/delete operation + if ((opr.getValue().getAsString().equalsIgnoreCase("modify") || opr.getValue().getAsString().equalsIgnoreCase("delete")) + && (vertexPayload.getId() == null)) { + + throw new CrudException("Mising ID at item: " + item.getKey(), Status.BAD_REQUEST); + + } + + vertices.add(item.getKey()); + } + + for (JsonElement v : payload.getRelationships()) { + List> entries = new ArrayList>(v.getAsJsonObject().entrySet()); + + if (entries.size() != 2) { + throw new CrudException("", Status.BAD_REQUEST); + } + Map.Entry opr = entries.get(0); + Map.Entry item = entries.get(1); + + if (edges.contains(item.getKey())) { + throw new CrudException("duplicate Edge in payload: " + item.getKey(), Status.BAD_REQUEST); + } + + EdgePayload edgePayload = EdgePayload.fromJson(item.getValue().getAsJsonObject().toString()); + + if (edgePayload.getType() == null) { + throw new CrudException("Edge Type cannot be null for: " + item.getKey(), Status.BAD_REQUEST); + } + + if (!opr.getKey().equalsIgnoreCase("operation")) { + throw new CrudException("operation missing in item: " + item.getKey(), Status.BAD_REQUEST); + } + + if (!opr.getValue().getAsString().equalsIgnoreCase("add") && !opr.getValue().getAsString().equalsIgnoreCase("modify") + && !opr.getValue().getAsString().equalsIgnoreCase("delete")) { + throw new CrudException("Invalid operation at item: " + item.getKey(), Status.BAD_REQUEST); + } + // check if ID is populate for modify/delete operation + if ((edgePayload.getId() == null) && (opr.getValue().getAsString().equalsIgnoreCase("modify") + || opr.getValue().getAsString().equalsIgnoreCase("delete"))) { + + throw new CrudException("Mising ID at item: " + item.getKey(), Status.BAD_REQUEST); + + } + if (opr.getValue().getAsString().equalsIgnoreCase("add")) { + if(edgePayload.getSource()==null || edgePayload.getTarget()==null){ + throw new CrudException("Source/Target cannot be null for edge: " + item.getKey(), + Status.BAD_REQUEST); + } + if (edgePayload.getSource().startsWith("$") && !vertices.contains(edgePayload.getSource().substring(1))) { + throw new CrudException("Source Vertex " + edgePayload.getSource().substring(1) + " not found for Edge: " + item.getKey(), + Status.BAD_REQUEST); + } + + if (edgePayload.getTarget().startsWith("$") && !vertices.contains(edgePayload.getTarget().substring(1))) { + throw new CrudException("Target Vertex " + edgePayload.getSource().substring(1) + " not found for Edge: " + item.getKey(), + Status.BAD_REQUEST); + } + } + edges.add(item.getKey()); + + } + + } + + @POST + @Path("/{version}/bulk/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response addBulk(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("uri") @Encoded String uri, + @Context HttpHeaders headers, @Context UriInfo uriInfo, + @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.POST, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + BulkPayload payload = BulkPayload.fromJson(content); + if ((payload.getObjects() == null && payload.getRelationships() == null) || (payload.getObjects() != null && payload.getObjects().isEmpty() + && payload.getRelationships() != null && payload.getRelationships().isEmpty())) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + + validateBulkPayload(payload); + String result = crudGraphDataService.addBulk(version, payload); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + LoggingUtil.logRestRequest(logger, auditLogger, req, response); return response; } @@ -636,20 +762,6 @@ public class CrudRestService { return response; } - @DELETE - @Path("/relationships/{version}/{type}/") - @Consumes({MediaType.APPLICATION_JSON}) - @Produces({MediaType.APPLICATION_JSON}) - public Response deleteEdgeWithouId(String content, @Context HttpHeaders headers, @Context HttpServletRequest req) { - - LoggingUtil.initMdcContext(req, headers); - - logger.debug("Incoming request..." + content); - Response response = Response.status ( Status.BAD_REQUEST ).entity ( "Cannot Delete Edge Without Specifying Id in URL" ).build (); - LoggingUtil.logRestRequest(logger, auditLogger, req, response); - return response; - } - @DELETE @Path("/relationships/{version}/{type}/{id}") @Consumes({MediaType.APPLICATION_JSON}) -- cgit 1.2.3-korg