From 908b4693e5a0a4c2f323dbf518b35e4620f183a1 Mon Sep 17 00:00:00 2001 From: "Bansal, Nitin (nb121v)" Date: Mon, 20 Nov 2017 16:02:06 -0500 Subject: Refactor to move from openecomp to onap Refactor to move from openecomp to onap IssueID: AAI-486 Change-Id: I1d5634739514acf11d5fbdf5e2c7865aaccd864e Signed-off-by: Bansal, Nitin (nb121v) --- src/main/java/org/onap/crud/dao/GraphDao.java | 183 +++++ .../java/org/onap/crud/dao/champ/ChampDao.java | 883 +++++++++++++++++++++ .../org/onap/crud/dao/champion/ChampionDao.java | 623 +++++++++++++++ .../crud/dao/champion/ChampionEdgeSerializer.java | 49 ++ .../dao/champion/ChampionVertexSerializer.java | 47 ++ 5 files changed, 1785 insertions(+) create mode 100644 src/main/java/org/onap/crud/dao/GraphDao.java create mode 100644 src/main/java/org/onap/crud/dao/champ/ChampDao.java create mode 100644 src/main/java/org/onap/crud/dao/champion/ChampionDao.java create mode 100644 src/main/java/org/onap/crud/dao/champion/ChampionEdgeSerializer.java create mode 100644 src/main/java/org/onap/crud/dao/champion/ChampionVertexSerializer.java (limited to 'src/main/java/org/onap/crud/dao') diff --git a/src/main/java/org/onap/crud/dao/GraphDao.java b/src/main/java/org/onap/crud/dao/GraphDao.java new file mode 100644 index 0000000..bc42b15 --- /dev/null +++ b/src/main/java/org/onap/crud/dao/GraphDao.java @@ -0,0 +1,183 @@ +/** + * ============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.onap.crud.dao; + +import java.util.List; +import java.util.Map; + +import org.onap.crud.entity.Edge; + +import org.onap.crud.entity.Vertex; +import org.onap.crud.exception.CrudException; + +public interface GraphDao { + + public Vertex getVertex(String id) throws CrudException; + + public Vertex getVertex(String id, String type) throws CrudException; + + /** + * Retrieve all of the edges which are incident to the vertex with the + * specified identifier. + * + * @param id + * - The unique identifier of the vertex to retrieve the edges for. + * @return - A collection of edges. + * @throws CrudException + */ + public List getVertexEdges(String id) throws CrudException; + + /** + * Retrieve a collection of {@link Vertex} objects which match the supplied + * type label and filter properties. + * + * @param type + * - The vertex type that we want to retrieve. + * @param filter + * - The parameters to filter our results by. + * @return - A collection of vertices. + * @throws CrudException + */ + public List getVertices(String type, Map filter) throws CrudException; + + /** + * Retrieve an {@link Edge} from the graph database by specifying its unique + * identifier. + * + * @param id + * - The unique identifier for the Edge to be retrieved. + * @return - The Edge corresponding to the specified identifier. + * @throws CrudException + */ + public Edge getEdge(String id, String type) throws CrudException; + + /** + * Retrieve a collection of {@link Edge} objects with a given type and which + * match a set of supplied filter parameters. + * + * @param type + * - The type of edges that we are interested in. + * @param filter + * - The parameters that we want to filter our edges by. + * @return - A collection of edges which match the supplied filter parameters. + * @throws CrudException + */ + public List getEdges(String type, Map filter) throws CrudException; + + /** + * Insert a new {@link Vertex} into the graph data store. + * + * @param type + * - The type label to assign to the vertex. + * @param properties + * - The properties to associated with this vertex. + * @return - The {@link Vertex} object that was created. + * @throws CrudException + */ + public Vertex addVertex(String type, Map properties) throws CrudException; + + /** + * Updates an existing {@link Vertex}. + * + * @param id + * - The unique identifier of the vertex to be updated. + * @param properties + * - The properties to associate with the vertex. + * @return - The udpated vertex. + * @throws CrudException + */ + public Vertex updateVertex(String id, String type, Map properties) throws CrudException; + + /** + * Removes the specified vertex from the graph data base. + * + *

+ * NOTE: The vertex MUST contain NO incident edges before it can be deleted. + * + * @param id + * - The unique identifier of the vertex to be deleted. + * @throws CrudException + */ + public void deleteVertex(String id, String type) throws CrudException; + + /** + * Adds an edge to the graph database. + * + * @param type + * - The 'type' label to apply to the edge. + * @param source + * - The source vertex for this edge. + * @param target + * - The target vertex for this edge. + * @param properties + * - The properties map to associate with this edge. + * @return - The {@link Edge} object that was created. + * @throws CrudException + */ + public Edge addEdge(String type, Vertex source, Vertex target, Map properties) throws CrudException; + + /** + * Updates an existing {@link Edge}. + * + * @param id + * - The unique identifier of the edge to be updated. + * @param properties + * - The properties to associate with the edge. + * @return - The update edge. + * @throws CrudException + */ + public Edge updateEdge(Edge edge) throws CrudException; + + /** + * Remove the specified edge from the graph data base. + * + * @param id + * - The unique identifier of the edge to be deleted. + * @throws CrudException + */ + public void deleteEdge(String id, String type) throws CrudException; + + public String openTransaction(); + + public void commitTransaction(String id) throws CrudException; + + public void rollbackTransaction(String id) throws CrudException; + + public boolean transactionExists(String id) throws CrudException; + + public Vertex addVertex(String type, Map properties, String txId) throws CrudException; + + public Edge addEdge(String type, Vertex source, Vertex target, Map properties, String txId) + throws CrudException; + + public Vertex updateVertex(String id, String type, Map properties, String txId) throws CrudException; + + public Edge updateEdge(Edge edge, String txId) throws CrudException; + + public void deleteVertex(String id, String type, String txId) throws CrudException; + + public void deleteEdge(String id, String type, String txId) throws CrudException; + + public Edge getEdge(String id, String type, String txId) throws CrudException; +} diff --git a/src/main/java/org/onap/crud/dao/champ/ChampDao.java b/src/main/java/org/onap/crud/dao/champ/ChampDao.java new file mode 100644 index 0000000..f217897 --- /dev/null +++ b/src/main/java/org/onap/crud/dao/champ/ChampDao.java @@ -0,0 +1,883 @@ +/** + * ============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.onap.crud.dao.champ; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.onap.aai.champcore.ChampGraph; +import org.onap.aai.champcore.ChampTransaction; +import org.onap.aai.champcore.exceptions.ChampMarshallingException; +import org.onap.aai.champcore.exceptions.ChampObjectNotExistsException; +import org.onap.aai.champcore.exceptions.ChampRelationshipNotExistsException; +import org.onap.aai.champcore.exceptions.ChampSchemaViolationException; +import org.onap.aai.champcore.exceptions.ChampTransactionException; +import org.onap.aai.champcore.exceptions.ChampUnmarshallingException; +import org.onap.aai.champcore.model.ChampObject; +import org.onap.aai.champcore.model.ChampRelationship; +import org.onap.aai.champcore.model.fluent.object.ObjectBuildOrPropertiesStep; +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +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.logging.CrudServiceMsgs; + +/** + * This is the integration layer between the CRUD API service and the low level + * Champ library for graph database interaction. + */ +public class ChampDao implements GraphDao { + + public static final String CONFIG_STORAGE_BACKEND = "storage.backend"; + public static final String CONFIG_STORAGE_BACKEND_DB = "storage.backend.db"; + public static final String STORAGE_HBASE_DB = "hbase"; + public static final String STORAGE_CASSANDRA_DB = "cassandra"; + public static final String CONFIG_STORAGE_HOSTNAMES = "storage.hostnames"; + public static final String CONFIG_STORAGE_PORT = "storage.port"; + public static final String CONFIG_HBASE_ZNODE_PARENT = "storage.hbase.ext.zookeeper.znode.parent"; + public static final String CONFIG_GRAPH_NAME = "graph.name"; + public static final String GRAPH_UNQ_INSTANCE_ID_SUFFIX = "graph.unique-instance-id-suffix"; + + public static final String CONFIG_EVENT_STREAM_PUBLISHER = "event.stream.publisher"; + public static final String CONFIG_EVENT_STREAM_NUM_PUBLISHERS = "event.stream.num-publishers"; + + private static Map transactions = new ConcurrentHashMap(); + public static final String DEFAULT_GRAPH_NAME = "default_graph"; + + private enum GraphType { + IN_MEMORY, TITAN, DSE + } + + /** + * Instance of the API used for interacting with the Champ library. + */ + private ChampGraph champApi = null; + + private Logger logger = LoggerFactory.getInstance().getLogger(ChampDao.class.getName()); + + /** + * Creates a new instance of the ChampDao. + * + * @param champGraph + * - Concrete implementation of the graph dao layer + */ + public ChampDao(ChampGraph champGraph) { + this.champApi = champGraph; + } + + @Override + public Vertex getVertex(String id) throws CrudException { + + try { + + if (logger.isDebugEnabled()) { + logger.debug("getVertex with id: " + id); + } + + long idAsLong = Long.parseLong(id); + + Optional retrievedVertex = champApi.retrieveObject(idAsLong); + + String nodeType = org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(); + if (retrievedVertex.isPresent() && retrievedVertex.get().getProperties().get(nodeType) != null) { + return vertexFromChampObject(retrievedVertex.get(), + retrievedVertex.get().getProperties().get(nodeType).toString()); + } else { + + // We didn't find a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + } catch (ChampUnmarshallingException | ChampTransactionException e) { + + // Something went wrong - throw an exception. + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public Vertex getVertex(String id, String type) throws CrudException { + + try { + + if (logger.isDebugEnabled()) { + logger.debug("getVertex with id: " + id); + } + + long idAsLong = Long.parseLong(id); + + // Request the vertex from the graph db. + Optional retrievedVertex = champApi.retrieveObject(idAsLong); + + // Did we find it? + if (retrievedVertex.isPresent() + && retrievedVertex.get().getProperties() + .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) != null + && retrievedVertex.get().getProperties() + .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString() + .equalsIgnoreCase(type)) { + + // Yup, convert it to a Vector object and return it. + return vertexFromChampObject(retrievedVertex.get(), type); + + } else { + + // We didn't find a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + } catch (ChampUnmarshallingException | ChampTransactionException e) { + + // Something went wrong - throw an exception. + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public List getVertexEdges(String id) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("get Edges incident to vertex with id: " + id + " from graph"); + } + + try { + long idAsLong = Long.parseLong(id); // GDF - what to do about id??? + + // Request the vertex from the graph db. + Optional retrievedVertex = champApi.retrieveObject(idAsLong); + + // Did we find it? + if (retrievedVertex.isPresent()) { + + // Query the Champ library for the edges which are incident to the + // specified + // vertex. + Stream relationships = champApi.retrieveRelationships(retrievedVertex.get()); + + // Build an edge list from the result stream. + List edges = new ArrayList(); + relationships.forEach(r -> edges.add(edgeFromChampRelationship(r))); + + return edges; + + } else { + + // We couldn't find the specified vertex, so throw an exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + } catch (ChampUnmarshallingException e) { + + // Something went wrong, so throw an exception. + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + + } catch (ChampObjectNotExistsException e) { + + // We couldn't find the specified vertex, so throw an exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } catch (ChampTransactionException e) { + throw new CrudException("Transaction error occured", javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public Vertex addVertex(String type, Map properties) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Add/update vertex: {label: " + type + " properties:" + propertiesMapToString(properties)); + } + + // Add the aai_node_type so that AAI can read the data created by gizmo + properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + // Create an object to represent our vertex in the format expected by the + // Champ library. + ChampObject objectToCreate = buildChampObject(type, properties); + + try { + + // Ask the Champ library to store our vertex, placing the returned object + // into a + // list so that we can easily put that into our result object. + return vertexFromChampObject(champApi.storeObject(objectToCreate), type); + + } catch (ChampMarshallingException | ChampSchemaViolationException | ChampObjectNotExistsException + | ChampTransactionException e) { + + // Something went wrong - throw an exception. + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public Vertex updateVertex(String id, String type, Map properties) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Update vertex with id: " + id + " with properties: " + propertiesMapToString(properties)); + } + // Add the aai_node_type so that AAI can read the data created by gizmo + properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + try { + // Now, build the updated version of the Champ Object... + ChampObject updateObject = buildChampObject(id, type, properties); + // ...and send it to the Champ library. + return vertexFromChampObject(champApi.replaceObject(updateObject), type); + + } catch (ChampObjectNotExistsException e) { + throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND); + } catch (NumberFormatException | ChampMarshallingException | ChampSchemaViolationException e) { + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } catch (ChampTransactionException e) { + throw new CrudException("Transaction error occured", javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + + } + + @Override + public List getVertices(String type, Map filter) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Retrieve vertices with type label: " + type + " which map query parameters: " + + propertiesMapToString(filter)); + } + + filter.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + Stream retrievedVertices; + try { + retrievedVertices = champApi.queryObjects(filter); + + } catch (ChampTransactionException e) { + throw new CrudException("Transaction error occured", javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + + List vertices = retrievedVertices.map(v -> vertexFromChampObject(v, type)).collect(Collectors.toList()); + + if (logger.isDebugEnabled()) { + logger.debug("Resulting vertex list: " + retrievedVertices); + } + + // ...and return it to the caller. + return vertices; + } + + private Object getRelKey(String id) { + Object key = id; + // convert into Long if applicable . TODO : revisit in story NUC-304 + try { + key = Long.parseLong(id); + } catch (NumberFormatException e) { + // The id isn't a Long, leave it as a string + } + + return key; + } + + @Override + public Edge getEdge(String id, String type) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Get edge with id: " + id); + } + + try { + + // Request the edge from the graph db. + Optional relationship = champApi.retrieveRelationship(getRelKey(id)); + + // Did we find it? + if (relationship.isPresent() && relationship.get().getType().equals(type)) { + + // Yup - return the result. + return edgeFromChampRelationship(relationship.get()); + + } else { + + // We didn't find an edge with the supplied id, so throw an exception. + throw new CrudException("No edge with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + } catch (ChampUnmarshallingException | ChampTransactionException e) { + + // Something went wrong, so throw an exception. + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public Edge addEdge(String type, Vertex source, Vertex target, Map properties) throws CrudException { + + // For now, assume source and target are straight ids... + try { + + Optional sourceObject = champApi.retrieveObject(Long.parseLong(source.getId().get())); + if (!sourceObject.isPresent() || !sourceObject.get().getType().equals(source.getType())) { + throw new CrudException( + "Error creating edge - source vertex with id " + source + " does not exist in graph data base", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + Optional targetObject = champApi.retrieveObject(Long.parseLong(target.getId().get())); + if (!targetObject.isPresent() || !targetObject.get().getType().equals(target.getType())) { + throw new CrudException( + "Error creating edge - target vertex with id " + target + " does not exist in graph data base", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + // Now, create the ChampRelationship object for our edge and store it in + // the graph database. + return edgeFromChampRelationship(champApi.storeRelationship( + new ChampRelationship.Builder(sourceObject.get(), targetObject.get(), type).properties(properties).build())); + + } catch (ChampMarshallingException | ChampObjectNotExistsException | ChampSchemaViolationException + | ChampRelationshipNotExistsException | ChampUnmarshallingException | NumberFormatException + | ChampTransactionException e) { + + throw new CrudException("Error creating edge: " + e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public List getEdges(String type, Map filter) throws CrudException { + + filter.put(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), type); + + Stream retrievedRelationships; + try { + retrievedRelationships = champApi.queryRelationships(filter); + + } catch (ChampTransactionException e) { + throw new CrudException("Transaction error occured", javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + + // Process the result stream from the Champ library into an Edge list, + // keeping only + // edges of the specified type. + List edges = retrievedRelationships.map(r -> edgeFromChampRelationship(r)).collect(Collectors.toList()); + + return edges; + } + + @Override + public Edge updateEdge(Edge edge) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug( + "Update edge with id: " + edge.getId() + " with properties: " + propertiesMapToString(edge.getProperties())); + } + + try { + // Now, build the updated version of the Champ Relationship... + ChampRelationship updateRelationship = new ChampRelationship.Builder( + buildChampObject(edge.getSource().getId().get(), edge.getSource().getType(), + edge.getSource().getProperties()), + buildChampObject(edge.getTarget().getId().get(), edge.getTarget().getType(), + edge.getTarget().getProperties()), + edge.getType()).key(getRelKey(edge.getId().get())).properties(edge.getProperties()).build(); + // ...and send it to the Champ library. + return edgeFromChampRelationship(champApi.replaceRelationship(updateRelationship)); + + } catch (ChampRelationshipNotExistsException ex) { + throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND); + } catch (NumberFormatException | ChampUnmarshallingException | ChampMarshallingException + | ChampSchemaViolationException | ChampTransactionException ex) { + + throw new CrudException(ex.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public void deleteVertex(String id, String type) throws CrudException { + + try { + + // First, retrieve the vertex that we intend to delete. + Optional retrievedVertex = champApi.retrieveObject(Long.parseLong(id)); + + // Did we find it? + if (!retrievedVertex.isPresent() || !retrievedVertex.get().getType().equals(type)) { + throw new CrudException("Failed to delete vertex with id: " + id + " - vertex does not exist.", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + // Now, verify that there are no edges incident to the vertex (they must + // be deleted + // first if so). + Stream relationships = champApi.retrieveRelationships(retrievedVertex.get()); + + if (relationships.count() > 0) { + throw new CrudException("Attempt to delete vertex with id " + id + " which has incident edges.", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + // Finally, we can attempt to delete our vertex. + champApi.deleteObject(Long.parseLong(id)); + + } catch (NumberFormatException | ChampUnmarshallingException | ChampObjectNotExistsException + | ChampTransactionException e) { + + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public void deleteEdge(String id, String type) throws CrudException { + + try { + + // First, retrieve the edge that we want to delete. + Optional relationshipToDelete = champApi.retrieveRelationship(getRelKey(id)); + + // Did we find it? + if (!relationshipToDelete.isPresent() || !relationshipToDelete.get().getType().equals(type)) { + throw new CrudException("Failed to delete edge with id: " + id + " - edge does not exist", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + // Now we can delete the edge. + champApi.deleteRelationship(relationshipToDelete.get()); + + } catch (ChampRelationshipNotExistsException | NumberFormatException | ChampUnmarshallingException + | ChampTransactionException e) { + + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + /** + * This helper method generates a string representation of a properties map + * for logging purposes. + * + * @param properties + * - The properties map to be converted. + * @return - The log statement friendly conversion of the properties map. + */ + private String propertiesMapToString(Map properties) { + + StringBuilder sb = new StringBuilder(); + sb.append("{"); + + for (String key : properties.keySet()) { + sb.append("(").append(key).append(" -> ").append(properties.get(key)).append(") "); + } + + sb.append("}"); + + return sb.toString(); + } + + /** + * This helper method constructs a {@link ChampObject} suitable for passing to + * the Champ library. + * + * @param type + * - The type to assign to our ChampObject + * @param properties + * - The set of properties to assign to our ChampObject + * @return - A populated ChampObject + */ + private ChampObject buildChampObject(String type, Map properties) { + + ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create().ofType(type).withoutKey(); + + for (String key : properties.keySet()) { + objectInProgress.withProperty(key, properties.get(key)); + } + return objectInProgress.build(); + } + + /** + * This helper method constructs a {@link ChampObject} suitable for passing to + * the Champ library. + * + * @param id + * - Unique identifier for this object. + * @param type + * - The type to assign to our ChampObject + * @param properties + * - The set of properties to assign to our ChampObject + * @return - A populated ChampObject + */ + private ChampObject buildChampObject(String id, String type, Map properties) { + + ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create().ofType(type).withKey(Long.parseLong(id)); + + for (String key : properties.keySet()) { + objectInProgress.withProperty(key, properties.get(key)); + } + return objectInProgress.build(); + } + + private Vertex vertexFromChampObject(ChampObject champObject, String type) { + + // Get the identifier for this vertex from the Champ object. + Object id = champObject.getKey().orElse(""); + + // Start building our {@link Vertex} object. + Vertex.Builder vertexBuilder = new Vertex.Builder(type); + vertexBuilder.id(id.toString()); + + // Convert the properties associated with the Champ object into the form + // expected for + // a Vertex object. + for (String key : champObject.getProperties().keySet()) { + vertexBuilder.property(key, champObject.getProperties().get(key)); + } + + // ...and return it. + return vertexBuilder.build(); + } + + /** + * This helper method converts a {@link ChampRelationship} from the Champ + * library into an equivalent {@link Edge} object that is understood by the + * CRUD Service. + * + * @param relationship + * - The ChampRelationship object to be converted. + * @return - An Edge object corresponding to the supplied ChampRelationship + */ + private Edge edgeFromChampRelationship(ChampRelationship relationship) { + + // Populate the edge's id, if available. + Object relationshipId = relationship.getKey().orElse(""); + + Edge.Builder edgeBuilder = new Edge.Builder(relationship.getType()).id(relationshipId.toString()); + edgeBuilder.source(vertexFromChampObject(relationship.getSource(), + relationship.getSource().getProperties() + .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null + ? relationship.getSource().getType() + : relationship.getSource().getProperties() + .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString())); + edgeBuilder.target(vertexFromChampObject(relationship.getTarget(), + relationship.getTarget().getProperties() + .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null + ? relationship.getTarget().getType() + : relationship.getTarget().getProperties() + .get(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString())); + + for (String key : relationship.getProperties().keySet()) { + edgeBuilder.property(key, relationship.getProperties().get(key).toString()); + } + + return edgeBuilder.build(); + } + + /** + * Performs any necessary shut down operations when the DAO is no longer + * needed. + */ + public void close() { + + if (champApi != null) { + + logger.info(CrudServiceMsgs.STOPPING_CHAMP_DAO); + + champApi.shutdown(); + } + } + + @Override + public String openTransaction() { + + ChampTransaction transaction = champApi.openTransaction(); + + transactions.put(transaction.id(), transaction); + logger.info(CrudServiceMsgs.TRANSACTION, "Stored transaction " + transaction.id() + " in hashmap"); + logger.info(CrudServiceMsgs.TRANSACTION, "Hash map contents:"); + for (String key : transactions.keySet()) { + logger.info(CrudServiceMsgs.TRANSACTION, key); + } + return transaction.id(); + } + + @Override + public void commitTransaction(String id) throws CrudException { + + try { + champApi.commitTransaction(getTransaction(id)); + } catch (ChampTransactionException e) { + throw new CrudException("Error while commiting transaction " + id, + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + transactions.remove(id); + } + + @Override + public void rollbackTransaction(String id) throws CrudException { + + try { + champApi.rollbackTransaction(getTransaction(id)); + } catch (ChampTransactionException e) { + throw new CrudException("Error while transaction rollback " + id, + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + transactions.remove(id); + } + + private ChampTransaction getTransaction(String id) throws CrudException { + + logger.info(CrudServiceMsgs.TRANSACTION, "Looking up transaction " + id); + if (transactions.containsKey(id)) { + logger.info(CrudServiceMsgs.TRANSACTION, "Found it!"); + return (transactions.get(id)); + } else { + logger.info(CrudServiceMsgs.TRANSACTION, "Didn't find transaction id " + id + ". Hash map contains: "); + for (String key : transactions.keySet()) { + logger.info(CrudServiceMsgs.TRANSACTION, key); + } + throw new CrudException("No open transaction with id: " + id, javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + @Override + public Vertex addVertex(String type, Map properties, String txId) throws CrudException { + if (logger.isDebugEnabled()) { + logger.debug("Add/update vertex: {label: " + type + " properties:" + propertiesMapToString(properties)); + } + + // Add the aai_node_type so that AAI can read the data created by gizmo + properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + // Create an object to represent our vertex in the format expected by the + // Champ library. + ChampObject objectToCreate = buildChampObject(type, properties); + + try { + + // Ask the Champ library to store our vertex, placing the returned object + // into a + // list so that we can easily put that into our result object. + return vertexFromChampObject(champApi.storeObject(objectToCreate, Optional.of(getTransaction(txId))), type); + + } catch (ChampMarshallingException | ChampSchemaViolationException | ChampObjectNotExistsException + | ChampTransactionException e) { + + // Something went wrong - throw an exception. + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public Edge addEdge(String type, Vertex source, Vertex target, Map properties, String txId) + throws CrudException { + // For now, assume source and target are straight ids... + try { + + Optional sourceObject = champApi.retrieveObject(Long.parseLong(source.getId().get()), + Optional.of(getTransaction(txId))); + if (!sourceObject.isPresent() || !sourceObject.get().getType().equals(source.getType())) { + throw new CrudException( + "Error creating edge - source vertex with id " + source + " does not exist in graph data base", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + Optional targetObject = champApi.retrieveObject(Long.parseLong(target.getId().get()), + Optional.of(getTransaction(txId))); + if (!targetObject.isPresent() || !targetObject.get().getType().equals(target.getType())) { + throw new CrudException( + "Error creating edge - target vertex with id " + target + " does not exist in graph data base", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + // Now, create the ChampRelationship object for our edge and store it in + // the graph database. + return edgeFromChampRelationship(champApi.storeRelationship( + new ChampRelationship.Builder(sourceObject.get(), targetObject.get(), type).properties(properties).build(), + Optional.of(getTransaction(txId)))); + + } catch (ChampMarshallingException | ChampObjectNotExistsException | ChampSchemaViolationException + | ChampTransactionException | ChampRelationshipNotExistsException | ChampUnmarshallingException e) { + + throw new CrudException("Error creating edge: " + e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + + } + + @Override + public Vertex updateVertex(String id, String type, Map properties, String txId) throws CrudException { + if (logger.isDebugEnabled()) { + logger.debug("Update vertex with id: " + id + " with properties: " + propertiesMapToString(properties)); + } + // Add the aai_node_type so that AAI can read the data created by gizmo + properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + try { + // Now, build the updated version of the Champ Object... + ChampObject updateObject = buildChampObject(id, type, properties); + // ...and send it to the Champ library. + return vertexFromChampObject(champApi.replaceObject(updateObject, Optional.of(getTransaction(txId))), type); + + } catch (ChampObjectNotExistsException e) { + throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND); + } catch (NumberFormatException | ChampMarshallingException | ChampTransactionException + | ChampSchemaViolationException e) { + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public boolean transactionExists(String id) throws CrudException { + return transactions.containsKey(id); + } + + @Override + public void deleteVertex(String id, String type, String txId) throws CrudException { + try { + + // First, retrieve the vertex that we intend to delete. + Optional retrievedVertex = champApi.retrieveObject(Long.parseLong(id), + Optional.of(getTransaction(txId))); + + // Did we find it? + if (!retrievedVertex.isPresent() || !retrievedVertex.get().getType().equals(type)) { + throw new CrudException("Failed to delete vertex with id: " + id + " - vertex does not exist.", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + // Now, verify that there are no edges incident to the vertex (they must + // be deleted + // first if so). + Stream relationships = champApi.retrieveRelationships(retrievedVertex.get(), + Optional.of(getTransaction(txId))); + + if (relationships.count() > 0) { + throw new CrudException("Attempt to delete vertex with id " + id + " which has incident edges.", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + // Finally, we can attempt to delete our vertex. + champApi.deleteObject(Long.parseLong(id), Optional.of(getTransaction(txId))); + + } catch (NumberFormatException | ChampUnmarshallingException | ChampObjectNotExistsException + | ChampTransactionException e) { + + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + + } + + @Override + public Edge updateEdge(Edge edge, String txId) throws CrudException { + if (logger.isDebugEnabled()) { + logger.debug( + "Update edge with id: " + edge.getId() + " with properties: " + propertiesMapToString(edge.getProperties())); + } + + try { + // Now, build the updated version of the Champ Relationship... + ChampRelationship updateRelationship = new ChampRelationship.Builder( + buildChampObject(edge.getSource().getId().get(), edge.getSource().getType(), + edge.getSource().getProperties()), + buildChampObject(edge.getTarget().getId().get(), edge.getTarget().getType(), + edge.getTarget().getProperties()), + edge.getType()).key(getRelKey(edge.getId().get())).properties(edge.getProperties()).build(); + // ...and send it to the Champ library. + return edgeFromChampRelationship( + champApi.replaceRelationship(updateRelationship, Optional.of(getTransaction(txId)))); + + } catch (ChampRelationshipNotExistsException ex) { + throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND); + } catch (NumberFormatException | ChampUnmarshallingException | ChampMarshallingException + | ChampSchemaViolationException | ChampTransactionException ex) { + + throw new CrudException(ex.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public void deleteEdge(String id, String type, String txId) throws CrudException { + try { + + // First, retrieve the edge that we want to delete. + Optional relationshipToDelete = champApi.retrieveRelationship(getRelKey(id), + Optional.of(getTransaction(txId))); + + // Did we find it? + if (!relationshipToDelete.isPresent() || !relationshipToDelete.get().getType().equals(type)) { + throw new CrudException("Failed to delete edge with id: " + id + " - edge does not exist", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + // Now we can delete the edge. + champApi.deleteRelationship(relationshipToDelete.get(), Optional.of(getTransaction(txId))); + + } catch (ChampRelationshipNotExistsException | NumberFormatException | ChampUnmarshallingException + | ChampTransactionException e) { + + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + + } + + @Override + public Edge getEdge(String id, String type, String txId) throws CrudException { + if (logger.isDebugEnabled()) { + logger.debug("Get edge with id: " + id); + } + + try { + + // Request the edge from the graph db. + Optional relationship = champApi.retrieveRelationship(getRelKey(id), + Optional.of(getTransaction(txId))); + + // Did we find it? + if (relationship.isPresent() && relationship.get().getType().equals(type)) { + + // Yup - return the result. + return edgeFromChampRelationship(relationship.get()); + + } else { + + // We didn't find an edge with the supplied id, so throw an exception. + throw new CrudException("No edge with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + } catch (ChampUnmarshallingException | ChampTransactionException e) { + + // Something went wrong, so throw an exception. + throw new CrudException(e.getMessage(), javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + +} diff --git a/src/main/java/org/onap/crud/dao/champion/ChampionDao.java b/src/main/java/org/onap/crud/dao/champion/ChampionDao.java new file mode 100644 index 0000000..4bde2e0 --- /dev/null +++ b/src/main/java/org/onap/crud/dao/champion/ChampionDao.java @@ -0,0 +1,623 @@ +/** + * ============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.onap.crud.dao.champion; + +import net.dongliu.gson.GsonJava8TypeAdapterFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +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.logging.LoggingContext; +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +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.List; +import java.util.Map; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +public class ChampionDao implements GraphDao { + private RestClient client; + private String baseUrl; + + private static final String HEADER_FROM_APP = "X-FromAppId"; + private static final String HEADER_TRANS_ID = "X-TransactionId"; + + private Logger logger = LoggerFactory.getInstance().getLogger(ChampionDao.class.getName()); + + // We use a custom vertex serializer for Champion because it expects "key" + // instead of "id" + private static final Gson championGson = new GsonBuilder() + .registerTypeAdapterFactory(new GsonJava8TypeAdapterFactory()) + .registerTypeAdapter(Vertex.class, new ChampionVertexSerializer()) + .registerTypeAdapter(Edge.class, new ChampionEdgeSerializer()).create(); + + public ChampionDao(String championUrl, String certPassword) { + try { + client = new RestClient().authenticationMode(RestAuthenticationMode.SSL_CERT).validateServerHostname(false) + .validateServerCertChain(false).clientCertFile(CrudServiceConstants.CRD_CHAMPION_AUTH_FILE) + .clientCertPassword(Password.deobfuscate(certPassword)); + + baseUrl = championUrl; + } catch (Exception e) { + System.out.println("Error setting up Champion configuration"); + e.printStackTrace(); + System.exit(1); + } + } + + @Override + public Vertex getVertex(String id) throws CrudException { + String url = baseUrl + "objects/" + id; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.get(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == 200) { + return Vertex.fromJson(getResult.getResult()); + } else { + // We didn't find a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + @Override + public Vertex getVertex(String id, String type) throws CrudException { + String url = baseUrl + "objects/" + id; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.get(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == 200) { + Vertex vert = Vertex.fromJson(getResult.getResult()); + + if (!vert.getType().equalsIgnoreCase(type)) { + // We didn't find a vertex with the supplied type, so just throw an + // exception. + throw new CrudException("No vertex with id " + id + "and type " + type + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + return vert; + } else { + // We didn't find a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + @Override + public List getVertexEdges(String id) throws CrudException { + String url = baseUrl + "objects/relationships/" + id; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.get(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == 200) { + return championGson.fromJson(getResult.getResult(), new TypeToken>() { + }.getType()); + } else { + // We didn't find a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + @Override + public List getVertices(String type, Map filter) throws CrudException { + filter.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + String url = baseUrl + "objects/filter" + "?" + + URLEncodedUtils.format(convertToNameValuePair(filter), Charset.defaultCharset()); + + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.get(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == 200) { + return championGson.fromJson(getResult.getResult(), new TypeToken>() { + }.getType()); + } else { + // We didn't find a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("No vertices found in graph for given filters", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + @Override + public Edge getEdge(String id, String type) throws CrudException { + String url = baseUrl + "relationships/" + id; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.get(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == 200) { + Edge edge = Edge.fromJson(getResult.getResult()); + + if (!edge.getType().equalsIgnoreCase(type)) { + // We didn't find an edge with the supplied type, so just throw an + // exception. + throw new CrudException("No edge with id " + id + "and type " + type + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + return edge; + } else { + // We didn't find a edge with the supplied type, so just throw an + // exception. + throw new CrudException("No edge with id " + id + " found in graph", javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + @Override + public List getEdges(String type, Map filter) throws CrudException { + String url = baseUrl + "relationships/filter" + "?" + + URLEncodedUtils.format(convertToNameValuePair(filter), Charset.defaultCharset()); + + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.get(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == 200) { + return championGson.fromJson(getResult.getResult(), new TypeToken>() { + }.getType()); + } else { + // We didn't find a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("No edges found in graph for given filters", javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + @Override + public Vertex addVertex(String type, Map properties) throws CrudException { + String url = baseUrl + "objects"; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + // Add the aai_node_type so that AAI can read the data created by gizmo + // TODO: This probably shouldn't be here + properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + Vertex.Builder insertVertexBuilder = new Vertex.Builder(type); + properties.forEach(insertVertexBuilder::property); + Vertex insertVertex = insertVertexBuilder.build(); + + OperationResult getResult = client.post(url, insertVertex.toJson(), headers, MediaType.APPLICATION_JSON_TYPE, + MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == Response.Status.CREATED.getStatusCode()) { + return Vertex.fromJson(getResult.getResult()); + } else { + // We didn't create a vertex with the supplied type, so just throw an + // exception. + throw new CrudException("Failed to create vertex", Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public Vertex updateVertex(String id, String type, Map properties) throws CrudException { + String url = baseUrl + "objects/" + id; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + // Add the aai_node_type so that AAI can read the data created by gizmo + // TODO: This probably shouldn't be here + properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + Vertex.Builder insertVertexBuilder = new Vertex.Builder(type); + insertVertexBuilder.id(id); + properties.forEach(insertVertexBuilder::property); + Vertex insertVertex = insertVertexBuilder.build(); + + String payload = insertVertex.toJson(championGson); + OperationResult getResult = client.put(url, payload, headers, MediaType.APPLICATION_JSON_TYPE, + MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == Response.Status.OK.getStatusCode()) { + return Vertex.fromJson(getResult.getResult()); + } else { + // We didn't create a vertex with the supplied type, so just throw an + // exception. + throw new CrudException("Failed to update vertex", Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public void deleteVertex(String id, String type) throws CrudException { + String url = baseUrl + "objects/" + id; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.delete(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() != Response.Status.OK.getStatusCode()) { + // We didn't delete a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("Failed to delete vertex", Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public Edge addEdge(String type, Vertex source, Vertex target, Map properties) throws CrudException { + String url = baseUrl + "relationships"; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + // Try requests to ensure source and target exist in Champion + Vertex dbSource = getVertex(source.getId().get(), source.getType()); + Vertex dbTarget = getVertex(target.getId().get(), target.getType()); + + Edge.Builder insertEdgeBuilder = new Edge.Builder(type).source(dbSource).target(dbTarget); + properties.forEach(insertEdgeBuilder::property); + Edge insertEdge = insertEdgeBuilder.build(); + + String edgeJson = insertEdge.toJson(championGson); + OperationResult getResult = client.post(url, edgeJson, headers, MediaType.APPLICATION_JSON_TYPE, + MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == Response.Status.CREATED.getStatusCode()) { + return Edge.fromJson(getResult.getResult()); + } else { + // We didn't create an edge with the supplied type, so just throw an + // exception. + throw new CrudException("Failed to create edge", Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public Edge updateEdge(Edge edge) throws CrudException { + if (!edge.getId().isPresent()) { + throw new CrudException("Unable to identify edge: " + edge.toString(), Response.Status.BAD_REQUEST); + } + String url = baseUrl + "relationships/" + edge.getId().get(); + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + String edgeJson = edge.toJson(championGson); + OperationResult getResult = client.put(url, edgeJson, headers, MediaType.APPLICATION_JSON_TYPE, + MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == Response.Status.OK.getStatusCode()) { + return Edge.fromJson(getResult.getResult()); + } else { + // We didn't create an edge with the supplied type, so just throw an + // exception. + throw new CrudException("Failed to update edge", Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public void deleteEdge(String id, String type) throws CrudException { + String url = baseUrl + "relationships/" + id; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.delete(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() != 200) { + // We didn't find an edge with the supplied type, so just throw an + // exception. + throw new CrudException("No edge with id " + id + " found in graph", javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + @Override + public String openTransaction() { + String url = baseUrl + "transaction"; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.post(url, "", headers, MediaType.TEXT_PLAIN_TYPE, MediaType.TEXT_PLAIN_TYPE); + + if (getResult.getResultCode() == 200) { + return getResult.getResult(); + } else { + return null; + } + } + + @Override + public void commitTransaction(String id) throws CrudException { + String url = baseUrl + "transaction/" + id; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.put(url, "{\"method\": \"commit\"}", headers, MediaType.APPLICATION_JSON_TYPE, + MediaType.TEXT_PLAIN_TYPE); + + if (getResult.getResultCode() != 200) { + throw new CrudException("Unable to commit transaction", + Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public void rollbackTransaction(String id) throws CrudException { + String url = baseUrl + "transaction/" + id; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.put(url, "{\"method\": \"rollback\"}", headers, MediaType.APPLICATION_JSON_TYPE, + MediaType.TEXT_PLAIN_TYPE); + + if (getResult.getResultCode() != 200) { + throw new CrudException("Unable to rollback transaction", + Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public boolean transactionExists(String id) throws CrudException { + String url = baseUrl + "transaction/" + id; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.get(url, headers, MediaType.APPLICATION_JSON_TYPE); + + return getResult.getResultCode() == 200; + } + + @Override + public Vertex addVertex(String type, Map properties, String txId) throws CrudException { + String url = baseUrl + "objects?transactionId=" + txId; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + // Add the aai_node_type so that AAI can read the data created by gizmo + // TODO: This probably shouldn't be here + properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + Vertex.Builder insertVertexBuilder = new Vertex.Builder(type); + properties.forEach(insertVertexBuilder::property); + Vertex insertVertex = insertVertexBuilder.build(); + + OperationResult getResult = client.post(url, insertVertex.toJson(), headers, MediaType.APPLICATION_JSON_TYPE, + MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == Response.Status.CREATED.getStatusCode()) { + return Vertex.fromJson(getResult.getResult()); + } else { + // We didn't create a vertex with the supplied type, so just throw an + // exception. + throw new CrudException("Failed to create vertex", Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public Edge addEdge(String type, Vertex source, Vertex target, Map properties, String txId) + throws CrudException { + String url = baseUrl + "relationships?transactionId=" + txId; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + // Try requests to ensure source and target exist in Champion + Vertex dbSource = getVertex(source.getId().get(), source.getType(), txId); + Vertex dbTarget = getVertex(target.getId().get(), target.getType(), txId); + + Edge.Builder insertEdgeBuilder = new Edge.Builder(type).source(dbSource).target(dbTarget); + properties.forEach(insertEdgeBuilder::property); + Edge insertEdge = insertEdgeBuilder.build(); + + OperationResult getResult = client.post(url, insertEdge.toJson(championGson), headers, + MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == Response.Status.CREATED.getStatusCode()) { + return Edge.fromJson(getResult.getResult()); + } else { + // We didn't create an edge with the supplied type, so just throw an + // exception. + throw new CrudException("Failed to create edge", Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public Vertex updateVertex(String id, String type, Map properties, String txId) throws CrudException { + String url = baseUrl + "objects/" + id + "?transactionId=" + txId; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + // Add the aai_node_type so that AAI can read the data created by gizmo + // TODO: This probably shouldn't be here + properties.put(org.onap.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + Vertex.Builder insertVertexBuilder = new Vertex.Builder(type); + insertVertexBuilder.id(id); + properties.forEach(insertVertexBuilder::property); + Vertex insertVertex = insertVertexBuilder.build(); + + String payload = insertVertex.toJson(championGson); + OperationResult getResult = client.put(url, payload, headers, MediaType.APPLICATION_JSON_TYPE, + MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == Response.Status.OK.getStatusCode()) { + return Vertex.fromJson(getResult.getResult()); + } else { + // We didn't create a vertex with the supplied type, so just throw an + // exception. + throw new CrudException("Failed to update vertex", Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public void deleteVertex(String id, String type, String txId) throws CrudException { + String url = baseUrl + "objects/" + id + "?transactionId=" + txId; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.delete(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() != Response.Status.OK.getStatusCode()) { + // We didn't delete a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("Failed to delete vertex", Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public Edge updateEdge(Edge edge, String txId) throws CrudException { + if (!edge.getId().isPresent()) { + throw new CrudException("Unable to identify edge: " + edge.toString(), Response.Status.BAD_REQUEST); + } + String url = baseUrl + "relationships/" + edge.getId().get() + "?transactionId=" + txId; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.put(url, edge.toJson(championGson), headers, MediaType.APPLICATION_JSON_TYPE, + MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == Response.Status.OK.getStatusCode()) { + return Edge.fromJson(getResult.getResult()); + } else { + // We didn't create an edge with the supplied type, so just throw an + // exception. + throw new CrudException("Failed to update edge: " + getResult.getFailureCause(), + Response.Status.fromStatusCode(getResult.getResultCode())); + } + } + + @Override + public void deleteEdge(String id, String type, String txId) throws CrudException { + String url = baseUrl + "relationships/" + id + "?transactionId=" + txId; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.delete(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() != 200) { + // We didn't find an edge with the supplied type, so just throw an + // exception. + throw new CrudException("No edge with id " + id + " found in graph", javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + @Override + public Edge getEdge(String id, String type, String txId) throws CrudException { + String url = baseUrl + "relationships/" + id + "?transactionId=" + txId; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.get(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == 200) { + Edge edge = Edge.fromJson(getResult.getResult()); + + if (!edge.getType().equalsIgnoreCase(type)) { + // We didn't find an edge with the supplied type, so just throw an + // exception. + throw new CrudException("No edge with id " + id + "and type " + type + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + return edge; + } else { + // We didn't find an edge with the supplied id, so just throw an + // exception. + throw new CrudException("No edge with id " + id + " found in graph", javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + public Vertex getVertex(String id, String type, String txId) throws CrudException { + String url = baseUrl + "objects/" + id + "?transactionId=" + txId; + Map> headers = new HashMap<>(); + headers.put(HEADER_FROM_APP, Arrays.asList("Gizmo")); + headers.put(HEADER_TRANS_ID, Arrays.asList(MDC.get(LoggingContext.LoggingField.REQUEST_ID.toString()))); + + OperationResult getResult = client.get(url, headers, MediaType.APPLICATION_JSON_TYPE); + + if (getResult.getResultCode() == 200) { + Vertex vert = Vertex.fromJson(getResult.getResult()); + + if (!vert.getType().equalsIgnoreCase(type)) { + // We didn't find a vertex with the supplied type, so just throw an + // exception. + throw new CrudException("No vertex with id " + id + "and type " + type + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + return vert; + } else { + // We didn't find a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + // https://stackoverflow.com/questions/26942330/convert-mapstring-string-to-listnamevaluepair-is-this-the-most-efficient + private List convertToNameValuePair(Map pairs) { + List nvpList = new ArrayList<>(pairs.size()); + + pairs.forEach((key, value) -> nvpList.add(new BasicNameValuePair(key, value.toString()))); + + return nvpList; + } +} diff --git a/src/main/java/org/onap/crud/dao/champion/ChampionEdgeSerializer.java b/src/main/java/org/onap/crud/dao/champion/ChampionEdgeSerializer.java new file mode 100644 index 0000000..655d096 --- /dev/null +++ b/src/main/java/org/onap/crud/dao/champion/ChampionEdgeSerializer.java @@ -0,0 +1,49 @@ +/** + * ============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.onap.crud.dao.champion; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import org.onap.crud.entity.Edge; +import org.onap.crud.entity.Vertex; + +import java.lang.reflect.Type; + +public class ChampionEdgeSerializer implements JsonSerializer { + @Override + public JsonElement serialize(Edge edge, Type type, JsonSerializationContext jsonSerializationContext) { + final JsonObject edgeObj = new JsonObject(); + if (edge.getId().isPresent()) { + edgeObj.add("key", jsonSerializationContext.serialize(edge.getId().get())); + } + edgeObj.add("type", jsonSerializationContext.serialize(edge.getType())); + edgeObj.add("properties", jsonSerializationContext.serialize(edge.getProperties())); + edgeObj.add("source", jsonSerializationContext.serialize(edge.getSource())); + edgeObj.add("target", jsonSerializationContext.serialize(edge.getTarget())); + return edgeObj; + } +} diff --git a/src/main/java/org/onap/crud/dao/champion/ChampionVertexSerializer.java b/src/main/java/org/onap/crud/dao/champion/ChampionVertexSerializer.java new file mode 100644 index 0000000..904ad2e --- /dev/null +++ b/src/main/java/org/onap/crud/dao/champion/ChampionVertexSerializer.java @@ -0,0 +1,47 @@ +/** + * ============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.onap.crud.dao.champion; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import org.onap.crud.entity.Vertex; + +import java.lang.reflect.Type; + +public class ChampionVertexSerializer implements JsonSerializer { + @Override + public JsonElement serialize(Vertex vertex, Type type, JsonSerializationContext jsonSerializationContext) { + final JsonObject vertexObj = new JsonObject(); + if (vertex.getId().isPresent()) { + vertexObj.add("key", jsonSerializationContext.serialize(vertex.getId().get())); + } + vertexObj.add("type", jsonSerializationContext.serialize(vertex.getType())); + vertexObj.add("properties", jsonSerializationContext.serialize(vertex.getProperties())); + + return vertexObj; + } +} -- cgit 1.2.3-korg