diff options
author | Shwetank Dave <shwetank.dave@amdocs.com> | 2017-08-02 17:10:38 -0400 |
---|---|---|
committer | Shwetank Dave <shwetank.dave@amdocs.com> | 2017-08-03 11:28:39 -0400 |
commit | e9c94429a8ada3e55854515060df817945b73d87 (patch) | |
tree | 3c5a9ac766d99570d542c1996f1321c99e5498e5 /src/main/java | |
parent | 27ff75c77611a8bdae37c7c0092787938c63a786 (diff) |
[AAI-26] Adding gizmo data to the repository.
Change-Id: I183f837d45acbfe3c673fde1acf8768d5e3fd37b
Signed-off-by: Shwetank Dave <shwetank.dave@amdocs.com>
Diffstat (limited to 'src/main/java')
26 files changed, 4857 insertions, 0 deletions
diff --git a/src/main/java/org/openecomp/crud/dao/GraphDao.java b/src/main/java/org/openecomp/crud/dao/GraphDao.java new file mode 100644 index 0000000..20b568c --- /dev/null +++ b/src/main/java/org/openecomp/crud/dao/GraphDao.java @@ -0,0 +1,138 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.dao; + +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; + +import java.util.List; +import java.util.Map; + +public interface GraphDao { + + public Vertex getVertex(String id, String type) throws CrudException; + + /** + * Retrieve all of the edges which are incident to the vertex with the specified identifier. + * + * @param id - The unique identifier of the vertex to retrieve the edges for. + * @return - A collection of edges. + * @throws CrudException + */ + public List<Edge> getVertexEdges(String id) throws CrudException; + + /** + * Retrieve a collection of {@link Vertex} objects which match the supplied type label + * and filter properties. + * + * @param type - The vertex type that we want to retrieve. + * @param filter - The parameters to filter our results by. + * @return - A collection of vertices. + * @throws CrudException + */ + public List<Vertex> getVertices(String type, Map<String, Object> filter) throws CrudException; + + /** + * Retrieve an {@link Edge} from the graph database by specifying its unique identifier. + * + * @param id - The unique identifier for the Edge to be retrieved. + * @return - The Edge corresponding to the specified identifier. + * @throws CrudException + */ + public Edge getEdge(String id, String type) throws CrudException; + + /** + * Retrieve a collection of {@link Edge} objects with a given type and which match a set of + * supplied filter parameters. + * + * @param type - The type of edges that we are interested in. + * @param filter - The parameters that we want to filter our edges by. + * @return - A collection of edges which match the supplied filter parameters. + * @throws CrudException + */ + public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException; + + /** + * Insert a new {@link Vertex} into the graph data store. + * + * @param type - The type label to assign to the vertex. + * @param properties - The properties to associated with this vertex. + * @return - The {@link Vertex} object that was created. + * @throws CrudException + */ + public Vertex addVertex(String type, Map<String, Object> properties) throws CrudException; + + /** + * Updates an existing {@link Vertex}. + * + * @param id - The unique identifier of the vertex to be updated. + * @param properties - The properties to associate with the vertex. + * @return - The udpated vertex. + * @throws CrudException + */ + public Vertex updateVertex(String id, String type, Map<String, Object> properties) + throws CrudException; + + /** + * Removes the specified vertex from the graph data base. + * + * <p>NOTE: The vertex MUST contain NO incident edges before it can be deleted. + * + * @param id - The unique identifier of the vertex to be deleted. + * @throws CrudException + */ + public void deleteVertex(String id, String type) throws CrudException; + + /** + * Adds an edge to the graph database. + * + * @param type - The 'type' label to apply to the edge. + * @param source - The source vertex for this edge. + * @param target - The target vertex for this edge. + * @param properties - The properties map to associate with this edge. + * @return - The {@link Edge} object that was created. + * @throws CrudException + */ + public Edge addEdge(String type, Vertex source, Vertex target, Map<String, Object> properties) + throws CrudException; + + /** + * Updates an existing {@link Edge}. + * + * @param id - The unique identifier of the edge to be updated. + * @param properties - The properties to associate with the edge. + * @return - The update edge. + * @throws CrudException + */ + public Edge updateEdge(Edge edge) throws CrudException; + + /** + * Remove the specified edge from the graph data base. + * + * @param id - The unique identifier of the edge to be deleted. + * @throws CrudException + */ + public void deleteEdge(String id, String type) throws CrudException; +} diff --git a/src/main/java/org/openecomp/crud/dao/champ/ChampDao.java b/src/main/java/org/openecomp/crud/dao/champ/ChampDao.java new file mode 100644 index 0000000..ff0a332 --- /dev/null +++ b/src/main/java/org/openecomp/crud/dao/champ/ChampDao.java @@ -0,0 +1,750 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.dao.champ; + +import org.openecomp.aai.champ.ChampAPI; +import org.openecomp.aai.champ.ChampGraph; +import org.openecomp.aai.champ.exceptions.ChampMarshallingException; +import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException; +import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException; +import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException; +import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException; +import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl; +import org.openecomp.aai.champ.model.ChampObject; +import org.openecomp.aai.champ.model.ChampRelationship; +import org.openecomp.aai.champ.model.fluent.object.ObjectBuildOrPropertiesStep; +import org.openecomp.cl.api.Logger; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.crud.dao.GraphDao; +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.logging.CrudServiceMsgs; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +/** + * This is the integration layer between the CRUD API service and the low level Champ library + * for graph database interaction. + */ +public class ChampDao implements GraphDao { + + public static final String CONFIG_STORAGE_BACKEND = "storage.backend"; + public static final String CONFIG_STORAGE_BACKEND_DB = "storage.backend.db"; + public static final String STORAGE_HBASE_DB = "hbase"; + public static final String STORAGE_CASSANDRA_DB = "cassandra"; + public static final String CONFIG_STORAGE_HOSTNAMES = "storage.hostnames"; + public static final String CONFIG_STORAGE_PORT = "storage.port"; + public static final String CONFIG_HBASE_ZNODE_PARENT = "storage.hbase.ext.zookeeper.znode.parent"; + public static final String CONFIG_GRAPH_NAME = "graph.name"; + public static final String GRAPH_UNQ_INSTANCE_ID_SUFFIX = "graph.unique-instance-id-suffix"; + + public static final String CONFIG_EVENT_STREAM_PUBLISHER = "event.stream.publisher"; + public static final String CONFIG_EVENT_STREAM_NUM_PUBLISHERS = "event.stream.num-publishers"; + + + public static final String DEFAULT_GRAPH_NAME = "default_graph"; + + /** + * Set of configuration properties for the DAI. + */ + private Properties daoConfig; + + /** + * Instance of the API used for interacting with the Champ library. + */ + private ChampGraph champApi = null; + + private Logger logger = LoggerFactory.getInstance().getLogger(ChampDao.class.getName()); + + + /** + * Creates a new instance of the ChampDao. + * + * @param config - Set of configuration properties to be applied to this instance + * of the DAO. + */ + public ChampDao(Properties config) { + + // Store the configuration properties. + daoConfig = config; + + // Apply the configuration to the DAO. + configure(); + + } + + + @Override + public Vertex getVertex(String id, String type) throws CrudException { + + try { + + if (logger.isDebugEnabled()) { + logger.debug("getVertex with id: " + id); + } + + long idAsLong = Long.parseLong(id); + + // Request the vertex from the graph db. + Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong); + + // Did we find it? + if (retrievedVertex.isPresent() && retrievedVertex.get().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName())!=null && retrievedVertex.get().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString() + .equalsIgnoreCase(type)) { + + // Yup, convert it to a Vector object and return it. + return vertexFromChampObject(retrievedVertex.get(),type); + + } else { + + // We didn't find a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + } catch (ChampUnmarshallingException e) { + + // Something went wrong - throw an exception. + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + + @Override + public List<Edge> getVertexEdges(String id) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("get Edges incident to vertex with id: " + id + " from graph"); + } + + try { + long idAsLong = Long.parseLong(id); // GDF - what to do about id??? + + // Request the vertex from the graph db. + Optional<ChampObject> retrievedVertex = champApi.retrieveObject(idAsLong); + + // Did we find it? + if (retrievedVertex.isPresent()) { + + // Query the Champ library for the edges which are incident to the specified + // vertex. + Stream<ChampRelationship> relationships = + champApi.retrieveRelationships(retrievedVertex.get()); + + // Build an edge list from the result stream. + List<Edge> edges = new ArrayList<Edge>(); + relationships.forEach(r -> edges.add(edgeFromChampRelationship(r))); + + return edges; + + } else { + + // We couldn't find the specified vertex, so throw an exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + } catch (ChampUnmarshallingException e) { + + // Something went wrong, so throw an exception. + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + + } catch (ChampObjectNotExistsException e) { + + // We couldn't find the specified vertex, so throw an exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + + @Override + public Vertex addVertex(String type, Map<String, Object> properties) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Add/update vertex: {label: " + type + + " properties:" + propertiesMapToString(properties)); + } + + //Add the aai_node_type so that AAI can read the data created by gizmo + properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + // Create an object to represent our vertex in the format expected by the Champ library. + ChampObject objectToCreate = buildChampObject(type, properties); + + try { + + // Ask the Champ library to store our vertex, placing the returned object into a + // list so that we can easily put that into our result object. + return vertexFromChampObject(champApi.storeObject(objectToCreate),type); + + } catch (ChampMarshallingException + | ChampSchemaViolationException + | ChampObjectNotExistsException e) { + + // Something went wrong - throw an exception. + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + + @Override + public Vertex updateVertex(String id, String type, Map<String, Object> properties) + throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Update vertex with id: " + id + " with properties: " + + propertiesMapToString(properties)); + } + //Add the aai_node_type so that AAI can read the data created by gizmo + properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + try { + // Now, build the updated version of the Champ Object... + ChampObject updateObject = buildChampObject(id, type, properties); + // ...and send it to the Champ library. + return vertexFromChampObject(champApi.replaceObject(updateObject),type); + + } catch (ChampObjectNotExistsException e) { + throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND); + } catch (NumberFormatException | ChampMarshallingException | ChampSchemaViolationException e) { + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + + } + + + @Override + public List<Vertex> getVertices(String type, Map<String, Object> filter) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Retrieve vertices with type label: " + type + " which map query parameters: " + + propertiesMapToString(filter)); + } + + + filter.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + + Stream<ChampObject> retrievedVertices = champApi.queryObjects(filter); + + List<Vertex> vertices = retrievedVertices + .map(v -> vertexFromChampObject(v,type)) + .collect(Collectors.toList()); + + + if (logger.isDebugEnabled()) { + logger.debug("Resulting vertex list: " + retrievedVertices); + } + + // ...and return it to the caller. + return vertices; + } + + private Object getRelKey(String id) { + Object key = id; + // convert into Long if applicable . TODO : revisit in story NUC-304 + try { + key = Long.parseLong(id); + } catch (NumberFormatException e) { + // The id isn't a Long, leave it as a string + } + + return key; + } + + @Override + public Edge getEdge(String id, String type) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Get edge with id: " + id); + } + + try { + + // Request the edge from the graph db. + Optional<ChampRelationship> relationship = champApi.retrieveRelationship(getRelKey(id)); + + // Did we find it? + if (relationship.isPresent() && relationship.get().getType().equals(type)) { + + // Yup - return the result. + return edgeFromChampRelationship(relationship.get()); + + } else { + + // We didn't find an edge with the supplied id, so throw an exception. + throw new CrudException("No edge with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + } catch (ChampUnmarshallingException e) { + + // Something went wrong, so throw an exception. + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public Edge addEdge(String type, + Vertex source, + Vertex target, + Map<String, Object> properties) throws CrudException { + + // For now, assume source and target are straight ids... + try { + + Optional<ChampObject> sourceObject + = champApi.retrieveObject(Long.parseLong(source.getId().get())); + if (!sourceObject.isPresent() || !sourceObject.get().getType().equals(source.getType())) { + throw new CrudException("Error creating edge - source vertex with id " + source + + " does not exist in graph data base", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + Optional<ChampObject> targetObject + = champApi.retrieveObject(Long.parseLong(target.getId().get())); + if (!targetObject.isPresent() || !targetObject.get().getType().equals(target.getType())) { + throw new CrudException("Error creating edge - target vertex with id " + target + + " does not exist in graph data base", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + // Now, create the ChampRelationship object for our edge and store it in + // the graph database. + return edgeFromChampRelationship( + champApi.storeRelationship( + new ChampRelationship.Builder(sourceObject.get(), targetObject.get(), type) + .properties(properties) + .build())); + + } catch (ChampMarshallingException + | ChampObjectNotExistsException + | ChampSchemaViolationException + | ChampRelationshipNotExistsException + | ChampUnmarshallingException e) { + + throw new CrudException("Error creating edge: " + e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + + @Override + public List<Edge> getEdges(String type, Map<String, Object> filter) throws CrudException { + + filter.put(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), type); + + Stream<ChampRelationship> retrievedRelationships = champApi.queryRelationships(filter); + // Process the result stream from the Champ library into an Edge list, keeping only + // edges of the specified type. + List<Edge> edges = retrievedRelationships + .map(r -> edgeFromChampRelationship(r)) + .collect(Collectors.toList()); + + return edges; + } + + @Override + public Edge updateEdge(Edge edge) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Update edge with id: " + edge.getId() + " with properties: " + + propertiesMapToString(edge.getProperties())); + } + + try { + // Now, build the updated version of the Champ Relationship... + ChampRelationship updateRelationship = new ChampRelationship.Builder( + buildChampObject(edge.getSource().getId().get(), edge.getSource().getType(), + edge.getSource().getProperties()), + buildChampObject(edge.getTarget().getId().get(), edge.getTarget().getType(), + edge.getTarget().getProperties()), + edge.getType()).key(getRelKey(edge.getId().get())) + .properties(edge.getProperties()).build(); + // ...and send it to the Champ library. + return edgeFromChampRelationship(champApi.replaceRelationship(updateRelationship)); + + + } catch (ChampRelationshipNotExistsException ex) { + throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND); + } catch (NumberFormatException | ChampUnmarshallingException | ChampMarshallingException + | ChampSchemaViolationException ex) { + + throw new CrudException(ex.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public void deleteVertex(String id, String type) throws CrudException { + + try { + + // First, retrieve the vertex that we intend to delete. + Optional<ChampObject> retrievedVertex = champApi.retrieveObject(Long.parseLong(id)); + + // Did we find it? + if (!retrievedVertex.isPresent() || !retrievedVertex.get().getType().equals(type)) { + throw new CrudException("Failed to delete vertex with id: " + + id + " - vertex does not exist.", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + // Now, verify that there are no edges incident to the vertex (they must be deleted + // first if so). + Stream<ChampRelationship> relationships = + champApi.retrieveRelationships(retrievedVertex.get()); + + if (relationships.count() > 0) { + throw new CrudException("Attempt to delete vertex with id " + + id + " which has incident edges.", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + // Finally, we can attempt to delete our vertex. + champApi.deleteObject(Long.parseLong(id)); + + + } catch (NumberFormatException + | ChampUnmarshallingException + | ChampObjectNotExistsException e) { + + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public void deleteEdge(String id, String type) throws CrudException { + + try { + + // First, retrieve the edge that we want to delete. + Optional<ChampRelationship> relationshipToDelete + = champApi.retrieveRelationship(getRelKey(id)); + + + // Did we find it? + if (!relationshipToDelete.isPresent() || !relationshipToDelete.get().getType().equals(type)) { + throw new CrudException("Failed to delete edge with id: " + id + " - edge does not exist", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + // Now we can delete the edge. + champApi.deleteRelationship(relationshipToDelete.get()); + + } catch (ChampRelationshipNotExistsException + | NumberFormatException + | ChampUnmarshallingException e) { + + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + + /** + * This helper method generates a string representation of a properties map for + * logging purposes. + * + * @param properties - The properties map to be converted. + * @return - The log statement friendly conversion of the properties map. + */ + private String propertiesMapToString(Map<String, Object> properties) { + + StringBuilder sb = new StringBuilder(); + sb.append("{"); + + for (String key : properties.keySet()) { + sb.append("(").append(key).append(" -> ").append(properties.get(key)).append(") "); + } + + sb.append("}"); + + return sb.toString(); + } + + + /** + * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library. + * + * @param type - The type to assign to our ChampObject + * @param properties - The set of properties to assign to our ChampObject + * @return - A populated ChampObject + */ + private ChampObject buildChampObject(String type, Map<String, Object> properties) { + + ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create() + .ofType(type) + .withoutKey(); + + for (String key : properties.keySet()) { + objectInProgress.withProperty(key, properties.get(key)); + } + return objectInProgress.build(); + } + + + /** + * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library. + * + * @param id - Unique identifier for this object. + * @param type - The type to assign to our ChampObject + * @param properties - The set of properties to assign to our ChampObject + * @return - A populated ChampObject + */ + private ChampObject buildChampObject(String id, String type, Map<String, Object> properties) { + + ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create() + .ofType(type) + .withKey(Long.parseLong(id)); + + for (String key : properties.keySet()) { + objectInProgress.withProperty(key, properties.get(key)); + } + return objectInProgress.build(); + } + + + + + private Vertex vertexFromChampObject(ChampObject champObject, String type) { + + // Get the identifier for this vertex from the Champ object. + Object id = champObject.getKey().orElse(""); + + // Start building our {@link Vertex} object. + Vertex.Builder vertexBuilder = new Vertex.Builder(type); + vertexBuilder.id(id.toString()); + + // Convert the properties associated with the Champ object into the form expected for + // a Vertex object. + for (String key : champObject.getProperties().keySet()) { + vertexBuilder.property(key, champObject.getProperties().get(key)); + } + + // ...and return it. + return vertexBuilder.build(); + } + + + /** + * This helper method converts a {@link ChampRelationship} from the Champ library into an + * equivalent {@link Edge} object that is understood by the CRUD Service. + * + * @param relationship - The ChampRelationship object to be converted. + * @return - An Edge object corresponding to the supplied ChampRelationship + */ + private Edge edgeFromChampRelationship(ChampRelationship relationship) { + + // Populate the edge's id, if available. + Object relationshipId = relationship.getKey().orElse(""); + + Edge.Builder edgeBuilder = new Edge.Builder(relationship.getType()) + .id(relationshipId.toString()); + edgeBuilder.source(vertexFromChampObject(relationship.getSource(), + relationship.getSource().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null + ? relationship.getSource().getType() + : relationship.getSource().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString())); + edgeBuilder.target(vertexFromChampObject(relationship.getTarget(), + relationship.getTarget().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null + ? relationship.getTarget().getType() + : relationship.getTarget().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString())); + + for (String key : relationship.getProperties().keySet()) { + edgeBuilder.property(key, relationship.getProperties().get(key).toString()); + } + + return edgeBuilder.build(); + } + + + /** + * Performs all one-time configuration operations which are required when creating + * a new instance of the DAO. + */ + private void configure() { + + // Instantiate the Champ library API. + try { + + // Determine which back end we are using. + switch (getBackendTypeFromConfig()) { + + case IN_MEMORY: + + logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, + "In Memory", + daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME), + "Not applicable"); + + champApi = ChampGraph.Factory.newInstance(ChampGraph.Type.IN_MEMORY, + daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME)); + + break; + + case TITAN: + try { + String db = daoConfig.getProperty(CONFIG_STORAGE_BACKEND_DB); + Short graphIdSuffix = (short) new Random().nextInt(Short.MAX_VALUE); + logger.info(CrudServiceMsgs.TITAN_GRAPH_INFO, GRAPH_UNQ_INSTANCE_ID_SUFFIX + + ": = " + graphIdSuffix); + if (db.equalsIgnoreCase(STORAGE_CASSANDRA_DB)) { + logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, "Titan with cassandra backend", + daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME), + daoConfig.getProperty(CONFIG_STORAGE_HOSTNAMES)); + + TitanChampGraphImpl.Builder champApiBuilder = + new TitanChampGraphImpl.Builder(daoConfig.getProperty(CONFIG_GRAPH_NAME, + DEFAULT_GRAPH_NAME)) + .property("storage.backend", "cassandrathrift") + .property(GRAPH_UNQ_INSTANCE_ID_SUFFIX, graphIdSuffix) + .property("storage.hostname", daoConfig.get(CONFIG_STORAGE_HOSTNAMES)); + + if (daoConfig.containsKey(CONFIG_EVENT_STREAM_PUBLISHER)) { + champApiBuilder.property("champ.event.stream.publisher", + daoConfig.get(CONFIG_EVENT_STREAM_PUBLISHER)); + } + + if (daoConfig.containsKey(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)) { + champApiBuilder.property("champ.event.stream.publisher-pool-size", + daoConfig.get(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)); + } + + champApi = champApiBuilder.build(); + + } else if (db.equalsIgnoreCase(STORAGE_HBASE_DB)) { + + logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, "Titan with Hbase backend", + daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME), + daoConfig.getProperty(CONFIG_STORAGE_HOSTNAMES)); + TitanChampGraphImpl.Builder champApiBuilder = + new TitanChampGraphImpl.Builder(daoConfig + .getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME)) + .property("storage.backend", "hbase") + .property("storage.hbase.ext.zookeeper.znode.parent", + daoConfig.get(CONFIG_HBASE_ZNODE_PARENT)) + .property("storage.port", daoConfig.get(CONFIG_STORAGE_PORT)) + .property(GRAPH_UNQ_INSTANCE_ID_SUFFIX, graphIdSuffix) + .property("storage.hostname", daoConfig.get(CONFIG_STORAGE_HOSTNAMES)); + + if (daoConfig.containsKey(CONFIG_EVENT_STREAM_PUBLISHER)) { + champApiBuilder.property("champ.event.stream.publisher", + daoConfig.get(CONFIG_EVENT_STREAM_PUBLISHER)); + } + + if (daoConfig.containsKey(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)) { + champApiBuilder.property("champ.event.stream.publisher-pool-size", + daoConfig.get(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)); + } + champApi = champApiBuilder.build(); + } else { + logger.error(CrudServiceMsgs.INVALID_GRAPH_BACKEND, + daoConfig.getProperty(CONFIG_STORAGE_BACKEND_DB)); + } + + } catch (com.thinkaurelius.titan.core.TitanException e) { + + logger.error(CrudServiceMsgs.INSTANTIATE_GRAPH_BACKEND_ERR, "Titan", e.getMessage()); + } + + + break; + + default: + logger.error(CrudServiceMsgs.INVALID_GRAPH_BACKEND, + daoConfig.getProperty(CONFIG_STORAGE_BACKEND)); + break; + } + + } catch (CrudException e) { + logger.error(CrudServiceMsgs.INSTANTIATE_GRAPH_BACKEND_ERR, + daoConfig.getProperty(CONFIG_STORAGE_BACKEND), e.getMessage()); + } + } + + + /** + * Performs any necessary shut down operations when the DAO is no longer needed. + */ + public void close() { + + if (champApi != null) { + + logger.info(CrudServiceMsgs.STOPPING_CHAMP_DAO); + + champApi.shutdown(); + } + } + + + /** + * This helper function converts the 'graph back end type' config parameter into the + * corresponding {@link ChampAPI.Type}. + * + * @return - A {@link ChampAPI.Type} + * @throws CrudException + */ + private ChampGraph.Type getBackendTypeFromConfig() throws CrudException { + + // Get the back end type from the DAO's configuration properties. + String backend = daoConfig.getProperty(CONFIG_STORAGE_BACKEND, "in-memory"); + + // Now, find the appropriate ChampAPI type and return it. + if (backend.equals("in-memory")) { + return ChampGraph.Type.IN_MEMORY; + } else if (backend.equals("titan")) { + return ChampGraph.Type.TITAN; + } + + // If we are here, then whatever was in the config properties didn't match to a supported + // back end type, so just throw an exception and let the caller figure it out. + throw new CrudException("Invalid graph backend type '" + backend + "' specified.", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + +} diff --git a/src/main/java/org/openecomp/crud/entity/Edge.java b/src/main/java/org/openecomp/crud/entity/Edge.java new file mode 100644 index 0000000..803511f --- /dev/null +++ b/src/main/java/org/openecomp/crud/entity/Edge.java @@ -0,0 +1,130 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.entity; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class Edge { + private static final Gson gson = new GsonBuilder().create(); + + private final Optional<String> id; + private final String type; + private final Map<String, Object> properties; + private final Vertex source; + private final Vertex target; + + + private Edge(Builder builder) { + this.id = builder.id; + this.type = builder.type; + this.source = builder.source; + this.target = builder.target; + this.properties = builder.properties; + } + + public static class Builder { + private Optional<String> id = Optional.empty(); + private final String type; + private Vertex source; + private Vertex target; + private final Map<String, Object> properties = new HashMap<String, Object>(); + + public Builder(String type) { + if (type == null) { + throw new IllegalArgumentException("Type cannot be null"); + } + + this.type = type; + } + + public Builder id(String id) { + if (id == null) { + throw new IllegalArgumentException("id cannot be null"); + } + + this.id = Optional.of(id); + return this; + } + + public Builder property(String key, Object value) { + if (key == null || value == null) { + throw new IllegalArgumentException("Property key/value cannot be null"); + } + properties.put(key, value); + return this; + } + + public Builder source(Vertex source) { + this.source = source; + return this; + } + + public Builder target(Vertex target) { + this.target = target; + return this; + } + + public Edge build() { + return new Edge(this); + } + } + + + @Override + public String toString() { + return "Edge [id=" + id + ", type=" + type + ", properties=" + properties + + ", source=" + source + ", target=" + target + "]"; + } + + public String toJson() { + return gson.toJson(this); + } + + public Optional<String> getId() { + return id; + } + + public String getType() { + return type; + } + + public Map<String, Object> getProperties() { + return properties; + } + + public Vertex getSource() { + return source; + } + + public Vertex getTarget() { + return target; + } + + +} diff --git a/src/main/java/org/openecomp/crud/entity/Vertex.java b/src/main/java/org/openecomp/crud/entity/Vertex.java new file mode 100644 index 0000000..1b0b97d --- /dev/null +++ b/src/main/java/org/openecomp/crud/entity/Vertex.java @@ -0,0 +1,102 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.entity; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class Vertex { + private static final Gson gson = new GsonBuilder().create(); + + private final Optional<String> id; + private final String type; + private final Map<String, Object> properties; + + + private Vertex(Builder builder) { + this.id = builder.id; + this.type = builder.type; + this.properties = builder.properties; + } + + public static class Builder { + private Optional<String> id = Optional.empty(); + private final String type; + private final Map<String, Object> properties = new HashMap<String, Object>(); + + public Builder(String type) { + if (type == null) { + throw new IllegalArgumentException("Type cannot be null"); + } + this.type = type; + } + + public Builder id(String id) { + if (id == null) { + throw new IllegalArgumentException("id cannot be null"); + } + + this.id = Optional.of(id); + return this; + } + + public Builder property(String key, Object value) { + if (key == null || value == null) { + throw new IllegalArgumentException("Property key/value cannot be null"); + } + properties.put(key, value); + return this; + } + + public Vertex build() { + return new Vertex(this); + } + } + + public String toJson() { + return gson.toJson(this); + } + + @Override + public String toString() { + return "Vertex [id=" + id + ", type=" + type + ", properties=" + properties + "]"; + } + + public Optional<String> getId() { + return id; + } + + public String getType() { + return type; + } + + public Map<String, Object> getProperties() { + return properties; + } + +} diff --git a/src/main/java/org/openecomp/crud/event/GraphEvent.java b/src/main/java/org/openecomp/crud/event/GraphEvent.java new file mode 100644 index 0000000..392bf2d --- /dev/null +++ b/src/main/java/org/openecomp/crud/event/GraphEvent.java @@ -0,0 +1,235 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.event; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.SerializedName; + +import org.openecomp.crud.exception.CrudException; + +import javax.ws.rs.core.Response.Status; + +public class GraphEvent { + + public enum GraphEventOperation { + CREATE, UPDATE, DELETE + } + + public enum GraphEventResult { + SUCCESS, FAILURE + } + + private GraphEventOperation operation; + + @SerializedName("transaction-id") + private String transactionId; + + private long timestamp; + + private GraphEventVertex vertex; + + private GraphEventEdge edge; + + private GraphEventResult result; + + @SerializedName("error-message") + private String errorMessage; + + private Status httpErrorStatus; + + /** + * Marshaller/unmarshaller for converting to/from JSON. + */ + private static final Gson gson = new GsonBuilder().disableHtmlEscaping() + .setPrettyPrinting().create(); + + public static Builder builder(GraphEventOperation operation) { + return new Builder(operation); + } + + public GraphEventOperation getOperation() { + return operation; + } + + public String getTransactionId() { + return transactionId; + } + + public long getTimestamp() { + return timestamp; + } + + public GraphEventVertex getVertex() { + return vertex; + } + + public GraphEventEdge getEdge() { + return edge; + } + + public GraphEventResult getResult() { + return result; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setResult(GraphEventResult result) { + this.result = result; + } + + + public Status getHttpErrorStatus() { + return httpErrorStatus; + } + + public void setHttpErrorStatus(Status httpErrorStatus) { + this.httpErrorStatus = httpErrorStatus; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public void setVertex(GraphEventVertex vertex) { + this.vertex = vertex; + } + + public void setEdge(GraphEventEdge edge) { + this.edge = edge; + } + + /** + * Unmarshalls this Vertex object into a JSON string. + * + * @return - A JSON format string representation of this Vertex. + */ + public String toJson() { + return gson.toJson(this); + } + + /** + * Marshalls the provided JSON string into a Vertex object. + * + * @param json - The JSON string to produce the Vertex from. + * @return - A Vertex object. + * @throws SpikeException + */ + public static GraphEvent fromJson(String json) throws CrudException { + + try { + + // Make sure that we were actually provided a non-empty string + // before we + // go any further. + if (json == null || json.isEmpty()) { + throw new CrudException("Empty or null JSON string.", Status.BAD_REQUEST); + } + + // Marshall the string into a Vertex object. + return gson.fromJson(json, GraphEvent.class); + + } catch (Exception ex) { + throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST); + } + } + + @Override + public String toString() { + + return toJson(); + } + + public String getObjectKey() { + if (this.getVertex() != null) { + return this.getVertex().getId(); + } else if (this.getEdge() != null) { + return this.getEdge().getId(); + } + + return null; + + } + + public String getObjectType() { + if (this.getVertex() != null) { + return "vertex->" + this.getVertex().getType(); + } else if (this.getEdge() != null) { + return "edge->" + this.getEdge().getType(); + } + + return null; + + } + + public static class Builder { + + GraphEvent event = null; + + public Builder(GraphEventOperation operation) { + event = new GraphEvent(); + event.operation = operation; + } + + public Builder vertex(GraphEventVertex vertex) { + event.vertex = vertex; + return this; + } + + public Builder edge(GraphEventEdge edge) { + event.edge = edge; + return this; + } + + public Builder result(GraphEventResult result) { + event.result = result; + return this; + } + + public Builder errorMessage(String errorMessage) { + event.errorMessage = errorMessage; + return this; + } + + public Builder httpErrorStatus(Status httpErrorStatus) { + event.httpErrorStatus = httpErrorStatus; + return this; + } + + public GraphEvent build() { + + event.timestamp = System.currentTimeMillis(); + event.transactionId = java.util.UUID.randomUUID().toString(); + + return event; + } + } + +} diff --git a/src/main/java/org/openecomp/crud/event/GraphEventEdge.java b/src/main/java/org/openecomp/crud/event/GraphEventEdge.java new file mode 100644 index 0000000..aaf9b72 --- /dev/null +++ b/src/main/java/org/openecomp/crud/event/GraphEventEdge.java @@ -0,0 +1,212 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.event; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; + +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; + +import java.util.Map; +import javax.ws.rs.core.Response.Status; + +/** + * This class provides a generic representation of an Edge as provided by the + * graph data store. + */ +public class GraphEventEdge { + + /** + * The unique identifier used to identify this edge in the graph data store. + */ + @SerializedName("key") + private String id; + + @SerializedName("schema-version") + private String modelVersion; + + /** + * Type label assigned to this vertex. + */ + private String type; + + /** + * Source vertex for our edge. + */ + private GraphEventVertex source; + + /** + * Target vertex for our edge. + */ + private GraphEventVertex target; + + /** + * Map of all of the properties assigned to this vertex. + */ + private JsonElement properties; + + /** + * Marshaller/unmarshaller for converting to/from JSON. + */ + private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + public GraphEventEdge(String id, String modelVersion, String type, GraphEventVertex source, + GraphEventVertex target, JsonElement properties) { + this.id = id; + this.modelVersion = modelVersion; + this.type = type; + this.source = source; + this.target = target; + this.properties = properties; + } + + public GraphEventEdge() { + + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public GraphEventVertex getSource() { + return source; + } + + public void setSource(GraphEventVertex source) { + this.source = source; + } + + public GraphEventVertex getTarget() { + return target; + } + + public void setTarget(GraphEventVertex target) { + this.target = target; + } + + public JsonElement getProperties() { + return properties; + } + + public void setProperties(JsonElement properties) { + this.properties = properties; + } + + public String getModelVersion() { + return modelVersion; + } + + public void setModelVersion(String modelVersion) { + this.modelVersion = modelVersion; + } + + /** + * Unmarshalls this Edge object into a JSON string. + * + * @return - A JSON format string representation of this Edge. + */ + public String toJson() { + return gson.toJson(this); + } + + /** + * Marshalls the provided JSON string into a Edge object. + * + * @param json - The JSON string to produce the Edge from. + * @return - A Edge object. + * @throws SpikeException + */ + public static GraphEventEdge fromJson(String json) throws CrudException { + + try { + + // Make sure that we were actually provided a non-empty string + // before we + // go any further. + if (json == null || json.isEmpty()) { + throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST); + } + + // Marshall the string into an Edge object. + return gson.fromJson(json, GraphEventEdge.class); + + } catch (Exception ex) { + throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST); + } + } + + public static GraphEventEdge fromEdge(Edge edge, String modelVersion) { + + java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType(); + JsonObject props = gson.toJsonTree(edge.getProperties(), mapType).getAsJsonObject(); + + GraphEventEdge graphEventEdge = new GraphEventEdge(edge.getId().orElse(""), modelVersion, + edge.getType(), new GraphEventVertex(edge.getSource().getId().orElse(""), null, + edge.getSource().getType(), null), new GraphEventVertex(edge.getTarget().getId().orElse(""), + null, edge.getTarget().getType(), null), props); + + return graphEventEdge; + + } + + public Edge toEdge() { + Edge.Builder builder = new Edge.Builder(this.getType()).id(this.getId()); + if (this.getSource() != null) { + builder.source(new Vertex.Builder(this.getSource().getType()).id(this.getSource().getId()) + .build()); + } + if (this.getTarget() != null) { + builder.target(new Vertex.Builder(this.getTarget().getType()).id(this.getTarget().getId()) + .build()); + } + + if (this.getProperties() != null) { + java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType(); + Map<String, Object> propertiesMap = gson.fromJson(this.getProperties(), mapType); + for (String key : propertiesMap.keySet()) { + builder.property(key, propertiesMap.get(key)); + } + } + return builder.build(); + + } +} diff --git a/src/main/java/org/openecomp/crud/event/GraphEventVertex.java b/src/main/java/org/openecomp/crud/event/GraphEventVertex.java new file mode 100644 index 0000000..7fde12a --- /dev/null +++ b/src/main/java/org/openecomp/crud/event/GraphEventVertex.java @@ -0,0 +1,181 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.event; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; + +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; + +import java.util.Map; +import javax.ws.rs.core.Response.Status; + +/** + * This class provides a generic representation of a Vertex as provided by the + * graph data store. + */ +public class GraphEventVertex { + + /** + * The unique identifier used to identify this vertex in the graph data + * store. + */ + @SerializedName("key") + private String id; + + @SerializedName("schema-version") + private String modelVersion; + + /** + * Type label assigned to this vertex. + */ + private String type; + + /** + * Map of all of the properties assigned to this vertex. + */ + private JsonElement properties; + + /** + * Marshaller/unmarshaller for converting to/from JSON. + */ + private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + public GraphEventVertex(String id, String modelVersion, String type, JsonElement properties) { + this.id = id; + this.modelVersion = modelVersion; + this.type = type; + this.properties = properties; + } + + public GraphEventVertex() { + + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + + public JsonElement getProperties() { + return properties; + } + + public void setProperties(JsonElement properties) { + this.properties = properties; + } + + public String getModelVersion() { + return modelVersion; + } + + public void setModelVersion(String modelVersion) { + this.modelVersion = modelVersion; + } + + /** + * Unmarshalls this Vertex object into a JSON string. + * + * @return - A JSON format string representation of this Vertex. + */ + public String toJson() { + return gson.toJson(this); + } + + /** + * Marshalls the provided JSON string into a Vertex object. + * + * @param json - The JSON string to produce the Vertex from. + * @return - A Vertex object. + * @throws SpikeException + */ + public static GraphEventVertex fromJson(String json) throws CrudException { + + try { + + // Make sure that we were actually provided a non-empty string + // before we + // go any further. + if (json == null || json.isEmpty()) { + throw new CrudException("Empty or null JSON string.", Status.BAD_REQUEST); + } + + // Marshall the string into a Vertex object. + return gson.fromJson(json, GraphEventVertex.class); + + } catch (Exception ex) { + throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST); + } + } + + @Override + public String toString() { + + return toJson(); + } + + public static GraphEventVertex fromVertex(Vertex vertex, String modelVersion) { + + java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType(); + JsonObject props = gson.toJsonTree(vertex.getProperties(), mapType).getAsJsonObject(); + GraphEventVertex graphEventVertex = new GraphEventVertex(vertex.getId().orElse(""), + modelVersion, vertex.getType(), props); + return graphEventVertex; + + } + + public Vertex toVertex() { + Vertex.Builder builder = new Vertex.Builder(this.getType()).id(this.getId()); + + if (this.getProperties() != null) { + java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>() {}.getType(); + Map<String, Object> propertiesMap = gson.fromJson(this.getProperties(), mapType); + for (String key : propertiesMap.keySet()) { + builder.property(key, propertiesMap.get(key)); + } + } + + return builder.build(); + + } + + +} diff --git a/src/main/java/org/openecomp/crud/exception/CrudException.java b/src/main/java/org/openecomp/crud/exception/CrudException.java new file mode 100644 index 0000000..2911070 --- /dev/null +++ b/src/main/java/org/openecomp/crud/exception/CrudException.java @@ -0,0 +1,62 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.exception; + +import javax.ws.rs.core.Response.Status; + +public class CrudException extends Exception { + + private static final long serialVersionUID = 8162385108397238865L; + + private Status httpStatus; + + public CrudException() { + } + + public CrudException(String message, Status httpStatus) { + super(message); + this.setHttpStatus(httpStatus); + } + + public CrudException(Throwable cause) { + super(cause); + } + + public CrudException(String message, Throwable cause) { + super(message, cause); + } + + public CrudException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public Status getHttpStatus() { + return httpStatus; + } + + public void setHttpStatus(Status httpStatus) { + this.httpStatus = httpStatus; + } +} diff --git a/src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java b/src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java new file mode 100644 index 0000000..71fb14b --- /dev/null +++ b/src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java @@ -0,0 +1,128 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.logging; + +import com.att.eelf.i18n.EELFResourceManager; + +import org.openecomp.cl.eelf.LogMessageEnum; + +public enum CrudServiceMsgs implements LogMessageEnum { + + /** + * Received request {0} {1} from {2}. Sending response: {3} + * + * <p>Arguments: + * {0} = operation + * {1} = target URL + * {2} = source + * {3} = response code + */ + PROCESS_REST_REQUEST, + + INVALID_OXM_FILE, + INVALID_OXM_DIR, + OXM_FILE_CHANGED, + + /** + * Successfully loaded schema: {0} + * + * <p>Arguments: + * {0} = oxm filename + */ + LOADED_OXM_FILE, + + /** + * Unable to load OXM schema: {0} + * + * <p>Arguments: + * {0} = error + */ + OXM_LOAD_ERROR, + + /** + * Instantiate data access layer for graph data store type: {0} graph: {1} using hosts: {2} + * + * <p>Arguments: + * {0} = Graph data store technology type + * {1} = Graph name + * {2} = Hosts list + */ + INSTANTIATE_GRAPH_DAO, + + /** + * Stopping ChampDAO... + * + * <p>Arguments: + */ + STOPPING_CHAMP_DAO, + + /** + * Unsupported graph database {0} specified. + * + * <p>Arguments: + * {0} = Graph database back end. + */ + INVALID_GRAPH_BACKEND, + + /** + * Failure instantiating {0} graph database backend. Cause: {1} + * + * <p>Arguments: + * {0} - Graph database type. + * {1} - Failure cause. + */ + INSTANTIATE_GRAPH_BACKEND_ERR, + + /** + * Failure instantiating CRUD Rest Service. Cause: {0} + * + * <p>Arguments: + * {0} - Failure cause. + */ + INSTANTIATE_AUTH_ERR, + + /** + * Any info log related to titan graph + * + * <p>Arguments: + * {0} - Info. + */ + TITAN_GRAPH_INFO, + + /** + * Arguments: + * {0} Opertaion + * {1} URI + * {2} = Exception + */ + EXCEPTION_DURING_METHOD_CALL; + + + /** + * Static initializer to ensure the resource bundles for this class are loaded... + */ + static { + EELFResourceManager.loadMessageBundle("logging/CrudServiceMsgs"); + } +} diff --git a/src/main/java/org/openecomp/crud/logging/LoggingUtil.java b/src/main/java/org/openecomp/crud/logging/LoggingUtil.java new file mode 100644 index 0000000..aaa250a --- /dev/null +++ b/src/main/java/org/openecomp/crud/logging/LoggingUtil.java @@ -0,0 +1,88 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.logging; + +import org.openecomp.cl.api.LogFields; +import org.openecomp.cl.api.LogLine; +import org.openecomp.cl.api.Logger; +import org.openecomp.cl.mdc.MdcContext; +import org.openecomp.crud.util.CrudServiceConstants; +import org.slf4j.MDC; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; + + +public class LoggingUtil { + /** + * Initializes mdc context. + */ + public static void initMdcContext(HttpServletRequest httpReq, HttpHeaders headers) { + String fromIp = httpReq.getRemoteAddr(); + String fromAppId = ""; + String transId = null; + + if (headers.getRequestHeaders().getFirst("X-FromAppId") != null) { + fromAppId = headers.getRequestHeaders().getFirst("X-FromAppId"); + } + + if ((headers.getRequestHeaders().getFirst("X-TransactionId") == null) + || headers.getRequestHeaders().getFirst("X-TransactionId").isEmpty()) { + transId = java.util.UUID.randomUUID().toString(); + } else { + transId = headers.getRequestHeaders().getFirst("X-TransactionId"); + } + + MdcContext.initialize(transId, CrudServiceConstants.CRD_SERVICE_NAME, "", fromAppId, fromIp); + } + + /** + * Logs the rest request. + */ + public static void logRestRequest(Logger logger, Logger auditLogger, + HttpServletRequest req, Response response) { + String respStatusString = ""; + if (Response.Status.fromStatusCode(response.getStatus()) != null) { + respStatusString = Response.Status.fromStatusCode(response.getStatus()).toString(); + } + + // Generate error log + logger.info(CrudServiceMsgs.PROCESS_REST_REQUEST, req.getMethod(), + req.getRequestURL().toString(), req.getRemoteHost(), + Integer.toString(response.getStatus())); + + // Generate audit log. + auditLogger.info(CrudServiceMsgs.PROCESS_REST_REQUEST, + new LogFields() + .setField(LogLine.DefinedFields.RESPONSE_CODE, response.getStatus()) + .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, respStatusString), + (req != null) ? req.getMethod() : "Unknown", + (req != null) ? req.getRequestURL().toString() : "Unknown", + (req != null) ? req.getRemoteHost() : "Unknown", + Integer.toString(response.getStatus()) + " error: " + (response.getEntity() == null ? "" + : response.getEntity().toString())); + MDC.clear(); + } +} diff --git a/src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java b/src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java new file mode 100644 index 0000000..1a84322 --- /dev/null +++ b/src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java @@ -0,0 +1,185 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.parser; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.service.EdgePayload; +import org.openecomp.crud.service.VertexPayload; +import org.openecomp.schema.RelationshipSchemaLoader; + +import java.util.ArrayList; +import java.util.List; + +public class CrudResponseBuilder { + + private static final Gson gson = new GsonBuilder().create(); + + public static final String SOURCE = "source"; + public static final String TARGET = "target"; + public static final String URL_BASE = "services/inventory/"; + + public static String buildUpsertVertexResponse(Vertex vertex, String version) + throws CrudException { + VertexPayload payload = new VertexPayload(); + payload.setId(vertex.getId().get()); + payload.setType(vertex.getType()); + payload.setUrl(URL_BASE + version + "/" + vertex.getType() + "/" + vertex.getId().get()); + JsonObject props = new JsonObject(); + for (String key : vertex.getProperties().keySet()) { + addJsonProperperty(props, key, vertex.getProperties().get(key)); + } + payload.setProperties(props); + return payload.toJson(); + } + + public static String buildUpsertEdgeResponse(Edge edge, String version) throws CrudException { + return buildGetEdgeResponse(edge, version); + } + + public static String buildGetVertexResponse(Vertex vertex, List<Edge> edges, String version) + throws CrudException { + VertexPayload vertexPayload = new VertexPayload(); + vertexPayload.setId(vertex.getId().get()); + vertexPayload.setType(vertex.getType()); + vertexPayload.setUrl(URL_BASE + version + "/" + vertex.getType() + "/" + vertex.getId().get()); + JsonObject props = new JsonObject(); + for (String key : vertex.getProperties().keySet()) { + addJsonProperperty(props, key, vertex.getProperties().get(key)); + } + vertexPayload.setProperties(props); + List<EdgePayload> inEdges = new ArrayList<EdgePayload>(); + List<EdgePayload> outEdges = new ArrayList<EdgePayload>(); + for (Edge e : edges) { + if (e.getTarget().getId().get().equals(vertex.getId().get())) { + EdgePayload inEdge = new EdgePayload(); + inEdge.setId(e.getId().get()); + inEdge.setType(e.getType()); + inEdge.setUrl(URL_BASE + "relationships/" + + RelationshipSchemaLoader.getLatestSchemaVersion() + + "/" + e.getType() + "/" + e.getId().get()); + inEdge.setSource( + URL_BASE + version + "/" + e.getSource().getType() + "/" + e.getSource().getId().get()); + + inEdges.add(inEdge); + } else if (e.getSource().getId().get().equals(vertex.getId().get())) { + EdgePayload outEdge = new EdgePayload(); + outEdge.setId(e.getId().get()); + outEdge.setType(e.getType()); + outEdge.setUrl(URL_BASE + "relationships/" + + RelationshipSchemaLoader.getLatestSchemaVersion() + + "/" + e.getType() + "/" + e.getId().get()); + outEdge.setTarget( + URL_BASE + version + "/" + e.getTarget().getType() + "/" + e.getTarget().getId().get()); + outEdges.add(outEdge); + } + } + + + vertexPayload.setIn(inEdges); + vertexPayload.setOut(outEdges); + + return vertexPayload.toJson(); + } + + public static String buildGetVerticesResponse(List<Vertex> items, String version) + throws CrudException { + + JsonArray arry = new JsonArray(); + for (Vertex v : items) { + JsonObject item = new JsonObject(); + item.addProperty("id", v.getId().get()); + item.addProperty("type", v.getType()); + item.addProperty("url", "services/inventory/" + version + "/" + + v.getType() + "/" + v.getId().get()); + + arry.add(item); + } + + return gson.toJson(arry); + } + + public static String buildGetEdgeResponse(Edge edge, String version) throws CrudException { + + EdgePayload payload = new EdgePayload(); + payload.setId(edge.getId().get()); + payload.setType(edge.getType()); + payload.setUrl(URL_BASE + "relationships/" + version + "/" + edge.getType() + + "/" + edge.getId().get()); + payload.setSource( + URL_BASE + version + "/" + edge.getSource().getType() + + "/" + edge.getSource().getId().get()); + payload.setTarget( + URL_BASE + version + "/" + edge.getTarget().getType() + + "/" + edge.getTarget().getId().get()); + + JsonObject props = new JsonObject(); + for (String key : edge.getProperties().keySet()) { + addJsonProperperty(props, key, edge.getProperties().get(key)); + } + payload.setProperties(props); + return payload.toJson(); + } + + public static String buildGetEdgesResponse(List<Edge> items, String version) + throws CrudException { + + JsonArray arry = new JsonArray(); + for (Edge e : items) { + JsonObject item = new JsonObject(); + item.addProperty("id", e.getId().get()); + item.addProperty("type", e.getType()); + item.addProperty("url", URL_BASE + "relationships/" + version + "/" + e.getType() + + "/" + e.getId().get()); + item.addProperty(SOURCE, "services/inventory/" + version + "/" + e.getSource().getType() + + "/" + e.getSource().getId().get()); + item.addProperty(TARGET, "services/inventory/" + version + "/" + e.getTarget().getType() + + "/" + e.getTarget().getId().get()); + arry.add(item); + } + + return gson.toJson(arry); + } + + private static void addJsonProperperty(JsonObject jsonObj, String key, Object value) { + if (value instanceof Integer) { + jsonObj.addProperty(key, (Integer) value); + } else if (value instanceof Boolean) { + jsonObj.addProperty(key, (Boolean) value); + } else if (value instanceof Double) { + jsonObj.addProperty(key, (Double) value); + } else if (value instanceof String) { + jsonObj.addProperty(key, (String) value); + } else { + jsonObj.addProperty(key, value.toString()); + } + } + +} diff --git a/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java b/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java new file mode 100644 index 0000000..ce60c3c --- /dev/null +++ b/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java @@ -0,0 +1,209 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.service; + +import com.att.ecomp.event.api.EventPublisher; + +import org.openecomp.crud.dao.GraphDao; +import org.openecomp.crud.dao.champ.ChampDao; +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.parser.CrudResponseBuilder; +import org.openecomp.crud.util.CrudProperties; +import org.openecomp.crud.util.CrudServiceConstants; +import org.openecomp.schema.OxmModelLoader; +import org.openecomp.schema.OxmModelValidator; +import org.openecomp.schema.RelationshipSchemaLoader; +import org.openecomp.schema.RelationshipSchemaValidator; + +import java.util.List; +import java.util.Map; +import java.util.Properties; + +public class CrudGraphDataService { + + private GraphDao dao; + + public CrudGraphDataService(EventPublisher champEventPublisher) throws CrudException { + + // Configure the GraphDao and wire it + Properties champProperties = new Properties(); + champProperties.put(ChampDao.CONFIG_STORAGE_BACKEND, "titan"); + champProperties.put(ChampDao.CONFIG_STORAGE_BACKEND_DB, + CrudProperties.get(CrudServiceConstants.CRD_STORAGE_BACKEND_DB, "hbase")); + champProperties.put(ChampDao.CONFIG_STORAGE_HOSTNAMES, + CrudProperties.get(CrudServiceConstants.CRD_GRAPH_HOST)); + champProperties.put(ChampDao.CONFIG_STORAGE_PORT, + CrudProperties.get(CrudServiceConstants.CRD_GRAPH_PORT, "2181")); + champProperties.put(ChampDao.CONFIG_HBASE_ZNODE_PARENT, + CrudProperties.get(CrudServiceConstants.CRD_HBASE_ZNODE_PARENT, "/hbase-unsecure")); + + if (CrudProperties.get("crud.graph.name") != null) { + champProperties.put(ChampDao.CONFIG_GRAPH_NAME, CrudProperties.get("crud.graph.name")); + } + + if (champEventPublisher != null) { + champProperties.put(ChampDao.CONFIG_EVENT_STREAM_PUBLISHER, champEventPublisher); + } + + if (CrudProperties.get(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS) != null) { + champProperties.put(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS, + Integer.parseInt(CrudProperties.get(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS))); + } + + ChampDao champDao = new ChampDao(champProperties); + + this.dao = champDao; + + //load the schemas + OxmModelLoader.loadModels(); + RelationshipSchemaLoader.loadModels(); + } + + + public String addVertex(String version, String type, VertexPayload payload) throws CrudException { + Vertex vertex = OxmModelValidator.validateIncomingUpsertPayload(null, version, type, + payload.getProperties()); + return addVertex(version, vertex); + } + + private String addVertex(String version, Vertex vertex) throws CrudException { + Vertex addedVertex = dao.addVertex(vertex.getType(), vertex.getProperties()); + return CrudResponseBuilder + .buildUpsertVertexResponse(OxmModelValidator.validateOutgoingPayload(version, addedVertex), + version); + } + + public String addEdge(String version, String type, EdgePayload payload) throws CrudException { + Edge edge = RelationshipSchemaValidator.validateIncomingAddPayload(version, type, payload); + return addEdge(version, edge); + } + + private String addEdge(String version, Edge edge) throws CrudException { + Edge addedEdge = dao.addEdge(edge.getType(), edge.getSource(), edge.getTarget(), + edge.getProperties()); + return CrudResponseBuilder.buildUpsertEdgeResponse( + RelationshipSchemaValidator.validateOutgoingPayload(version, addedEdge), version); + } + + public String getEdge(String version, String id, String type) throws CrudException { + RelationshipSchemaValidator.validateType(version, type); + Edge edge = dao.getEdge(id, type); + + return CrudResponseBuilder.buildGetEdgeResponse(RelationshipSchemaValidator + .validateOutgoingPayload(version, edge), + version); + } + + public String getEdges(String version, String type, Map<String, String> filter) + throws CrudException { + RelationshipSchemaValidator.validateType(version, type); + List<Edge> items = dao.getEdges(type, RelationshipSchemaValidator + .resolveCollectionfilter(version, type, filter)); + return CrudResponseBuilder.buildGetEdgesResponse(items, version); + } + + + public String updateVertex(String version, String id, String type, VertexPayload payload) + throws CrudException { + Vertex vertex = OxmModelValidator.validateIncomingUpsertPayload(id, version, type, + payload.getProperties()); + return updateVertex(version, vertex); + + } + + private String updateVertex(String version, Vertex vertex) throws CrudException { + Vertex updatedVertex = dao.updateVertex(vertex.getId().get(), vertex.getType(), + vertex.getProperties()); + return CrudResponseBuilder + .buildUpsertVertexResponse(OxmModelValidator.validateOutgoingPayload(version, + updatedVertex), version); + } + + public String patchVertex(String version, String id, String type, VertexPayload payload) + throws CrudException { + Vertex existingVertex = dao.getVertex(id, OxmModelValidator.resolveCollectionType(version, + type)); + Vertex vertex = OxmModelValidator.validateIncomingPatchPayload(id, version, type, + payload.getProperties(), existingVertex); + return updateVertex(version, vertex); + + } + + public String deleteVertex(String version, String id, String type) throws CrudException { + type = OxmModelValidator.resolveCollectionType(version, type); + dao.deleteVertex(id, type); + return ""; + + } + + public String deleteEdge(String version, String id, String type) throws CrudException { + RelationshipSchemaValidator.validateType(version, type); + dao.deleteEdge(id, type); + return ""; + + } + + public String updateEdge(String version, String id, String type, EdgePayload payload) + throws CrudException { + Edge edge = dao.getEdge(id, type); + Edge validatedEdge = RelationshipSchemaValidator.validateIncomingUpdatePayload(edge, + version, payload); + return updateEdge(version, validatedEdge); + + } + + private String updateEdge(String version, Edge edge) throws CrudException { + Edge updatedEdge = dao.updateEdge(edge); + return CrudResponseBuilder.buildUpsertEdgeResponse( + RelationshipSchemaValidator.validateOutgoingPayload(version, updatedEdge), version); + } + + public String patchEdge(String version, String id, String type, EdgePayload payload) + throws CrudException { + Edge edge = dao.getEdge(id, type); + Edge patchedEdge = RelationshipSchemaValidator.validateIncomingPatchPayload(edge, + version, payload); + return updateEdge(version, patchedEdge); + + } + + public String getVertex(String version, String id, String type) throws CrudException { + type = OxmModelValidator.resolveCollectionType(version, type); + Vertex vertex = dao.getVertex(id, type); + List<Edge> edges = dao.getVertexEdges(id); + return CrudResponseBuilder.buildGetVertexResponse(OxmModelValidator + .validateOutgoingPayload(version, vertex), edges, version); + } + + public String getVertices(String version, String type, Map<String, String> filter) + throws CrudException { + type = OxmModelValidator.resolveCollectionType(version, type); + List<Vertex> items = dao.getVertices(type, OxmModelValidator.resolveCollectionfilter(version, + type, filter)); + return CrudResponseBuilder.buildGetVerticesResponse(items, version); + } + +} diff --git a/src/main/java/org/openecomp/crud/service/CrudRestService.java b/src/main/java/org/openecomp/crud/service/CrudRestService.java new file mode 100644 index 0000000..09193ad --- /dev/null +++ b/src/main/java/org/openecomp/crud/service/CrudRestService.java @@ -0,0 +1,686 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.service; + +import org.apache.cxf.jaxrs.ext.PATCH; +import org.openecomp.auth.Auth; +import org.openecomp.cl.api.Logger; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.logging.CrudServiceMsgs; +import org.openecomp.crud.logging.LoggingUtil; +import org.openecomp.crud.util.CrudServiceConstants; +import org.slf4j.MDC; + +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.security.auth.x500.X500Principal; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.Encoded; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + +public class CrudRestService { + + private CrudGraphDataService crudGraphDataService; + Logger logger = LoggerFactory.getInstance().getLogger(CrudRestService.class.getName()); + Logger auditLogger = LoggerFactory.getInstance().getAuditLogger(CrudRestService.class.getName()); + private Auth auth; + + private String mediaType = MediaType.APPLICATION_JSON; + public static final String HTTP_PATCH_METHOD_OVERRIDE = "X-HTTP-Method-Override"; + + public CrudRestService(CrudGraphDataService crudGraphDataService) throws Exception { + this.crudGraphDataService = crudGraphDataService; + this.auth = new Auth(CrudServiceConstants.CRD_AUTH_FILE); + } + + public enum Action { + POST, GET, PUT, DELETE, PATCH + } + + ; + + public void startup() { + + } + + @GET + @Path("/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response getVertex(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + String result = crudGraphDataService.getVertex(version, id, type); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @GET + @Path("/{version}/{type}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response getVertices(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("uri") @Encoded String uri, + @Context HttpHeaders headers, @Context UriInfo uriInfo, + @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + Map<String, String> filter = new HashMap<String, String>(); + for (Map.Entry<String, List<String>> e : uriInfo.getQueryParameters().entrySet()) { + filter.put(e.getKey(), e.getValue().get(0)); + } + + try { + String result = crudGraphDataService.getVertices(version, type, filter); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @GET + @Path("/relationships/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response getEdge(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + + String result = crudGraphDataService.getEdge(version, id, type); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @GET + @Path("/relationships/{version}/{type}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response getEdges(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("uri") @Encoded String uri, + @Context HttpHeaders headers, @Context UriInfo uriInfo, + @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + Map<String, String> filter = new HashMap<String, String>(); + for (Map.Entry<String, List<String>> e : uriInfo.getQueryParameters().entrySet()) { + filter.put(e.getKey(), e.getValue().get(0)); + } + + try { + String result = crudGraphDataService.getEdges(version, type, filter); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @PUT + @Path("/relationships/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response updateEdge(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + EdgePayload payload = EdgePayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null && !payload.getId().equals(id)) { + throw new CrudException("ID Mismatch", Status.BAD_REQUEST); + } + String result; + + if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null + && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) + .equalsIgnoreCase("PATCH")) { + result = crudGraphDataService.patchEdge(version, id, type, payload); + } else { + + result = crudGraphDataService.updateEdge(version, id, type, payload); + } + + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @PATCH + @Path("/relationships/{version}/{type}/{id}") + @Consumes({"application/merge-patch+json"}) + @Produces({MediaType.APPLICATION_JSON}) + public Response patchEdge(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + if (validateRequest(req, uri, content, Action.PATCH, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + EdgePayload payload = EdgePayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null && !payload.getId().equals(id)) { + throw new CrudException("ID Mismatch", Status.BAD_REQUEST); + } + + String result = crudGraphDataService.patchEdge(version, id, type, payload); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @PUT + @Path("/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response updateVertex(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + VertexPayload payload = VertexPayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null && !payload.getId().equals(id)) { + throw new CrudException("ID Mismatch", Status.BAD_REQUEST); + } + String result; + if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null + && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) + .equalsIgnoreCase("PATCH")) { + result = crudGraphDataService.patchVertex(version, id, type, payload); + } else { + + result = crudGraphDataService.updateVertex(version, id, type, payload); + } + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @PATCH + @Path("/{version}/{type}/{id}") + @Consumes({"application/merge-patch+json"}) + @Produces({MediaType.APPLICATION_JSON}) + public Response patchVertex(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.PATCH, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + try { + VertexPayload payload = VertexPayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null && !payload.getId().equals(id)) { + throw new CrudException("ID Mismatch", Status.BAD_REQUEST); + } + + String result = crudGraphDataService.patchVertex(version, id, type, payload); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @POST + @Path("/{version}/{type}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response addVertex(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("uri") @Encoded String uri, + @Context HttpHeaders headers, @Context UriInfo uriInfo, + @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.POST, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + VertexPayload payload = VertexPayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null) { + throw new CrudException("ID specified , use Http PUT to update Vertex", + Status.BAD_REQUEST); + } + + if (payload.getType() != null && !payload.getType().equals(type)) { + throw new CrudException("Vertex Type mismatch", Status.BAD_REQUEST); + } + + String result = crudGraphDataService.addVertex(version, type, payload); + response = Response.status(Status.CREATED).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @POST + @Path("/{version}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response addVertex(String content, @PathParam("version") String version, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.POST, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + try { + + VertexPayload payload = VertexPayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null) { + throw new CrudException("ID specified , use Http PUT to update Vertex", + Status.BAD_REQUEST); + } + + if (payload.getType() == null || payload.getType().isEmpty()) { + throw new CrudException("Missing Vertex Type ", Status.BAD_REQUEST); + } + String result = crudGraphDataService.addVertex(version, payload.getType(), payload); + response = Response.status(Status.CREATED).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @POST + @Path("/relationships/{version}/{type}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response addEdge(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("uri") @Encoded String uri, + @Context HttpHeaders headers, @Context UriInfo uriInfo, + @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.POST, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + + try { + EdgePayload payload = EdgePayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null) { + throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST); + } + + if (payload.getType() != null && !payload.getType().equals(type)) { + throw new CrudException("Edge Type mismatch", Status.BAD_REQUEST); + } + String result = crudGraphDataService.addEdge(version, type, payload); + response = Response.status(Status.CREATED).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @POST + @Path("/relationships/{version}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response addEdge(String content, @PathParam("version") String version, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.POST, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + + try { + EdgePayload payload = EdgePayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null) { + throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST); + } + + if (payload.getType() == null || payload.getType().isEmpty()) { + throw new CrudException("Missing Edge Type ", Status.BAD_REQUEST); + } + String result = crudGraphDataService.addEdge(version, payload.getType(), payload); + + response = Response.status(Status.CREATED).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @DELETE + @Path("/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response deleteVertex(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.DELETE, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + + try { + String result = crudGraphDataService.deleteVertex(version, id, type); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @DELETE + @Path("/relationships/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response deleteEdge(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + if (validateRequest(req, uri, content, Action.DELETE, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + String result = crudGraphDataService.deleteEdge(version, id, type); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + protected boolean validateRequest(HttpServletRequest req, String uri, String content, + Action action, String authPolicyFunctionName) { + try { + String cipherSuite = (String) req.getAttribute("javax.servlet.request.cipher_suite"); + String authUser = null; + if (cipherSuite != null) { + X509Certificate[] certChain = (X509Certificate[]) req + .getAttribute("javax.servlet.request.X509Certificate"); + X509Certificate clientCert = certChain[0]; + X500Principal subjectDn = clientCert.getSubjectX500Principal(); + authUser = subjectDn.toString(); + } + return this.auth.validateRequest(authUser.toLowerCase(), action.toString() + + ":" + authPolicyFunctionName); + } catch (Exception e) { + logResult(action, uri, e); + return false; + } + } + + void logResult(Action op, String uri, Exception e) { + + logger.error(CrudServiceMsgs.EXCEPTION_DURING_METHOD_CALL, op.toString(), uri, + e.getStackTrace().toString()); + + // Clear the MDC context so that no other transaction inadvertently + // uses our transaction id. + MDC.clear(); + } +} diff --git a/src/main/java/org/openecomp/crud/service/EdgePayload.java b/src/main/java/org/openecomp/crud/service/EdgePayload.java new file mode 100644 index 0000000..d098d16 --- /dev/null +++ b/src/main/java/org/openecomp/crud/service/EdgePayload.java @@ -0,0 +1,115 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.service; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; + +import org.openecomp.crud.exception.CrudException; + +import javax.ws.rs.core.Response.Status; + +public class EdgePayload { + + private String id; + private String type; + private String url; + private String source; + private String target; + private JsonElement properties; + + private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + + @Override + public String toString() { + return "EdgePayload [id=" + id + ", type=" + type + ", url=" + url + ", source=" + + source + ", target=" + target + ", properties=" + properties + "]"; + } + + public String toJson() { + return gson.toJson(this); + } + + public static EdgePayload fromJson(String payload) throws CrudException { + try { + if (payload == null || payload.isEmpty()) { + throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST); + } + return gson.fromJson(payload, EdgePayload.class); + } catch (Exception ex) { + throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST); + } + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public JsonElement getProperties() { + return properties; + } + + public void setProperties(JsonElement properties) { + this.properties = properties; + } + +}
\ No newline at end of file diff --git a/src/main/java/org/openecomp/crud/service/JaxrsEchoService.java b/src/main/java/org/openecomp/crud/service/JaxrsEchoService.java new file mode 100644 index 0000000..0edd316 --- /dev/null +++ b/src/main/java/org/openecomp/crud/service/JaxrsEchoService.java @@ -0,0 +1,63 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.service; + +import org.openecomp.cl.api.Logger; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.crud.logging.LoggingUtil; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + + +public class JaxrsEchoService { + + private static Logger logger = LoggerFactory.getInstance() + .getLogger(JaxrsEchoService.class.getName()); + private static Logger auditLogger = LoggerFactory.getInstance() + .getAuditLogger(JaxrsEchoService.class.getName()); + + @GET + @Path("echo/{input}") + @Produces("text/plain") + public String ping(@PathParam("input") String input, + @Context HttpHeaders headers, + @Context UriInfo info, + @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + LoggingUtil.logRestRequest(logger, auditLogger, req, Response.status(Status.OK) + .entity("OK").build()); + + return "Hello, " + input + "."; + } +}
\ No newline at end of file diff --git a/src/main/java/org/openecomp/crud/service/VertexPayload.java b/src/main/java/org/openecomp/crud/service/VertexPayload.java new file mode 100644 index 0000000..ed79002 --- /dev/null +++ b/src/main/java/org/openecomp/crud/service/VertexPayload.java @@ -0,0 +1,117 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.service; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; + +import org.openecomp.crud.exception.CrudException; + +import java.util.ArrayList; +import java.util.List; +import javax.ws.rs.core.Response.Status; + +public class VertexPayload { + + private String id; + private String type; + private String url; + private JsonElement properties; + private List<EdgePayload> in = new ArrayList<EdgePayload>(); + private List<EdgePayload> out = new ArrayList<EdgePayload>(); + + private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + public String toJson() { + return gson.toJson(this); + } + + public static VertexPayload fromJson(String payload) throws CrudException { + try { + if (payload == null || payload.isEmpty()) { + throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST); + } + return gson.fromJson(payload, VertexPayload.class); + } catch (Exception ex) { + throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST); + } + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public JsonElement getProperties() { + return properties; + } + + public void setProperties(JsonElement properties) { + this.properties = properties; + } + + public List<EdgePayload> getIn() { + return in; + } + + public void setIn(List<EdgePayload> in) { + this.in = in; + } + + public List<EdgePayload> getOut() { + return out; + } + + public void setOut(List<EdgePayload> out) { + this.out = out; + } + + + @Override + public String toString() { + return "VertexPayload [id=" + id + ", type=" + type + ", url=" + url + ", properties=" + + properties + ", in=" + in + ", out=" + out + "]"; + } + +}
\ No newline at end of file diff --git a/src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java b/src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java new file mode 100644 index 0000000..c1a1e18 --- /dev/null +++ b/src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java @@ -0,0 +1,92 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.util; + +import org.eclipse.persistence.dynamic.DynamicEntity; +import org.eclipse.persistence.jaxb.JAXBMarshaller; +import org.eclipse.persistence.jaxb.MarshallerProperties; +import org.eclipse.persistence.jaxb.UnmarshallerProperties; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; + +import java.io.StringReader; +import java.io.StringWriter; +import javax.ws.rs.core.MediaType; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.transform.stream.StreamSource; + +public class CrudJaxbTransformation { + /** + * Marshal a dynamic entity into a string. + * + * @param entity the dynamic entity + * @param jaxbContext the dynamic jaxb context + * @return the marshaled entity + * @throws RouterException on error + */ + public static String marshal(MediaType mediaType, final DynamicEntity entity, + final DynamicJAXBContext jaxbContext) throws JAXBException { + + final JAXBMarshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty(JAXBMarshaller.JAXB_FORMATTED_OUTPUT, false); + + if (MediaType.APPLICATION_JSON_TYPE.isCompatible(mediaType)) { + marshaller.setProperty("eclipselink.media-type", "application/json"); + marshaller.setProperty("eclipselink.json.include-root", false); + marshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, Boolean.FALSE); + } + + final StringWriter writer = new StringWriter(); + marshaller.marshal(entity, writer); + return writer.toString(); + + } + + /** + * @param type + * @param json + * @param mediaType + * @return + * @throws JAXBException + * @throws Exception + */ + public static Object unmarshal(String javaClass, String content, MediaType mediaType, + final DynamicJAXBContext jaxbContext) throws JAXBException { + Object clazz = null; + Unmarshaller unmarshaller = null; + + clazz = jaxbContext.newDynamicEntity(javaClass); + + unmarshaller = jaxbContext.createUnmarshaller(); + if (mediaType.equals(MediaType.APPLICATION_JSON_TYPE)) { + unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json"); + unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false); + unmarshaller.setProperty(UnmarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true); + } + + return unmarshaller.unmarshal(new StreamSource(new StringReader(content)), + clazz.getClass()).getValue(); + } + +} diff --git a/src/main/java/org/openecomp/crud/util/CrudProperties.java b/src/main/java/org/openecomp/crud/util/CrudProperties.java new file mode 100644 index 0000000..69b2e16 --- /dev/null +++ b/src/main/java/org/openecomp/crud/util/CrudProperties.java @@ -0,0 +1,77 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +public class CrudProperties { + + private static Properties properties; + + static { + properties = new Properties(); + File file = new File(CrudServiceConstants.CRD_CONFIG_FILE); + try { + properties.load(new FileInputStream(file)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static String get(String key) { + return properties.getProperty(key); + } + + public static String get(String key, String defaultValue) { + return properties.getProperty(key, defaultValue); + } + + public static void put(String key, String value) { + properties.setProperty(key, value); + FileOutputStream fileOut = null; + try { + fileOut = new FileOutputStream(new File(CrudServiceConstants.CRD_CONFIG_FILE)); + properties.store(fileOut, "Added property: " + key); + } catch (Exception e) { + e.printStackTrace(); + } finally { + + try { + fileOut.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + } + + +} diff --git a/src/main/java/org/openecomp/crud/util/CrudServiceConstants.java b/src/main/java/org/openecomp/crud/util/CrudServiceConstants.java new file mode 100644 index 0000000..9543e2d --- /dev/null +++ b/src/main/java/org/openecomp/crud/util/CrudServiceConstants.java @@ -0,0 +1,53 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.util; + +public class CrudServiceConstants { + public static final String CRD_SERVICE_NAME = "Crud-Service"; + + public static final String CRD_FILESEP = (System.getProperty("file.separator") == null) ? "/" + : System.getProperty("file.separator"); + + public static final String CRD_SPECIFIC_CONFIG = System.getProperty("CONFIG_HOME") + CRD_FILESEP; + + public static final String CRD_HOME_MODEL = CRD_SPECIFIC_CONFIG + "model" + CRD_FILESEP; + public static final String CRD_HOME_AUTH = CRD_SPECIFIC_CONFIG + "auth" + CRD_FILESEP; + + public static final String CRD_GRAPH_HOST = "crud.graph.host"; + public static final String CRD_GRAPH_PORT = "crud.graph.port"; + public static final String CRD_GRAPH_NAME = "crud.graph.name"; + public static final String CRD_STORAGE_BACKEND_DB = "crud.storage.backend.db"; + public static final String CRD_HBASE_ZNODE_PARENT + = "crud.storage.hbase.ext.zookeeper.znode.parent"; + + public static final String CRD_CONFIG_FILE = CRD_SPECIFIC_CONFIG + "crud-api.properties"; + public static final String CRD_AUTH_FILE = CRD_HOME_AUTH + "crud_policy.json"; + + public static final String CRD_AUTH_POLICY_NAME = "crud"; + + public static final String CRD_EVENT_STREAM_HOSTS = "event.stream.hosts"; + + + +} diff --git a/src/main/java/org/openecomp/crud/util/CrudServiceUtil.java b/src/main/java/org/openecomp/crud/util/CrudServiceUtil.java new file mode 100644 index 0000000..25e3de6 --- /dev/null +++ b/src/main/java/org/openecomp/crud/util/CrudServiceUtil.java @@ -0,0 +1,56 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.util; + +import org.openecomp.crud.exception.CrudException; + +import javax.ws.rs.core.Response.Status; + +public class CrudServiceUtil { + + + public static Object validateFieldType(String value, Class clazz) throws CrudException { + try { + if (clazz.isAssignableFrom(Integer.class)) { + return Integer.parseInt(value); + } else if (clazz.isAssignableFrom(Long.class)) { + return Long.parseLong(value); + } else if (clazz.isAssignableFrom(Float.class)) { + return Float.parseFloat(value); + } else if (clazz.isAssignableFrom(Double.class)) { + return Double.parseDouble(value); + } else if (clazz.isAssignableFrom(Boolean.class)) { + if (!value.equals("true") && !value.equals("false")) { + throw new CrudException("Invalid propertry value: " + value, Status.BAD_REQUEST); + } + return Boolean.parseBoolean(value); + } else { + return value; + } + } catch (Exception e) { + throw new CrudException("Invalid property value: " + value, Status.BAD_REQUEST); + } + } + +} diff --git a/src/main/java/org/openecomp/crud/util/FileWatcher.java b/src/main/java/org/openecomp/crud/util/FileWatcher.java new file mode 100644 index 0000000..8c7cbc1 --- /dev/null +++ b/src/main/java/org/openecomp/crud/util/FileWatcher.java @@ -0,0 +1,48 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.util; + +import java.io.File; +import java.util.TimerTask; + +public abstract class FileWatcher extends TimerTask { + private long timeStamp; + private File file; + + public FileWatcher(File file) { + this.file = file; + this.timeStamp = file.lastModified(); + } + + public final void run() { + long timeStamp = file.lastModified(); + + if ((timeStamp - this.timeStamp) > 500) { + this.timeStamp = timeStamp; + onChange(file); + } + } + + protected abstract void onChange(File file); +}
\ No newline at end of file diff --git a/src/main/java/org/openecomp/schema/OxmModelLoader.java b/src/main/java/org/openecomp/schema/OxmModelLoader.java new file mode 100644 index 0000000..4ef77a2 --- /dev/null +++ b/src/main/java/org/openecomp/schema/OxmModelLoader.java @@ -0,0 +1,168 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.schema; + +import org.eclipse.persistence.jaxb.JAXBContextProperties; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.logging.CrudServiceMsgs; +import org.openecomp.crud.util.CrudServiceConstants; +import org.openecomp.crud.util.FileWatcher; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.ws.rs.core.Response.Status; +import javax.xml.bind.JAXBException; + + +public class OxmModelLoader { + + private static Map<String, DynamicJAXBContext> versionContextMap + = new ConcurrentHashMap<String, DynamicJAXBContext>(); + private static Map<String, Timer> timers = new ConcurrentHashMap<String, Timer>(); + + final static Pattern p = Pattern.compile("aai_oxm_(.*).xml"); + + private static org.openecomp.cl.api.Logger logger = LoggerFactory.getInstance() + .getLogger(OxmModelLoader.class.getName()); + + public synchronized static void loadModels() throws CrudException { + ClassLoader cl = OxmModelLoader.class.getClassLoader(); + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl); + Resource[] resources; + try { + resources = resolver.getResources("classpath*:/oxm/aai_oxm*.xml"); + } catch (IOException ex) { + logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, ex.getMessage()); + throw new CrudException("", Status.NOT_FOUND); + } + + if (resources.length == 0) { + logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, "No OXM schema files found on classpath"); + throw new CrudException("Failed to load schema", Status.NOT_FOUND); + } + + for (Resource resource : resources) { + Matcher matcher = p.matcher(resource.getFilename()); + + if (matcher.matches()) { + try { + OxmModelLoader.loadModel(matcher.group(1), resource); + } catch (Exception e) { + logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, "Failed to load " + resource.getFilename() + + ": " + e.getMessage()); + throw new CrudException("Failed to load schema", Status.NOT_FOUND); + } + } + } + } + + private static void addtimer(String version, File file) { + TimerTask task = null; + task = new FileWatcher( + file) { + protected void onChange(File file) { + // here we implement the onChange + logger.info(CrudServiceMsgs.OXM_FILE_CHANGED, file.getName()); + + try { + OxmModelLoader.loadModel(version, file); + } catch (Exception e) { + e.printStackTrace(); + } + + } + }; + + if (!timers.containsKey(version)) { + Timer timer = new Timer("oxm-" + version); + timer.schedule(task, new Date(), 10000); + timers.put(version, timer); + + } + } + + private synchronized static void loadModel(String version, File file) + throws JAXBException, IOException { + InputStream inputStream = new FileInputStream(file); + loadModel(version, file.getName(), inputStream); + addtimer(version, file); + } + + private synchronized static void loadModel(String version, Resource resource) + throws JAXBException, IOException { + InputStream inputStream = resource.getInputStream(); + loadModel(version, resource.getFilename(), inputStream); + } + + private synchronized static void loadModel(String version, String resourceName, + InputStream inputStream) + throws JAXBException, IOException { + Map<String, Object> properties = new HashMap<String, Object>(); + properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, inputStream); + final DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory + .createContextFromOXM(Thread.currentThread().getContextClassLoader(), properties); + versionContextMap.put(version, jaxbContext); + logger.info(CrudServiceMsgs.LOADED_OXM_FILE, resourceName); + } + + public static DynamicJAXBContext getContextForVersion(String version) throws CrudException { + if (versionContextMap == null || versionContextMap.isEmpty()) { + loadModels(); + } else if (!versionContextMap.containsKey(version)) { + try { + loadModel(version, new File(CrudServiceConstants.CRD_HOME_MODEL + "aai_oxm_" + + version + ".xml")); + } catch (Exception e) { + throw new CrudException("", Status.NOT_FOUND); + } + } + + return versionContextMap.get(version); + } + + public static Map<String, DynamicJAXBContext> getVersionContextMap() { + return versionContextMap; + } + + public static void setVersionContextMap(Map<String, DynamicJAXBContext> versionContextMap) { + OxmModelLoader.versionContextMap = versionContextMap; + } + +} diff --git a/src/main/java/org/openecomp/schema/OxmModelValidator.java b/src/main/java/org/openecomp/schema/OxmModelValidator.java new file mode 100644 index 0000000..45a2597 --- /dev/null +++ b/src/main/java/org/openecomp/schema/OxmModelValidator.java @@ -0,0 +1,325 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.schema; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + +import org.eclipse.persistence.dynamic.DynamicType; +import org.eclipse.persistence.internal.helper.DatabaseField; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; +import org.eclipse.persistence.mappings.DatabaseMapping; +import org.eclipse.persistence.oxm.XMLField; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.util.CrudServiceUtil; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.ws.rs.core.Response.Status; + +public class OxmModelValidator { + public enum Metadata { + NODE_TYPE("aai-node-type"), + URI("aai-uri"), + CREATED_TS("aai-created-ts"), + SOT("source-of-truth"), + LAST_MOD_SOT("last-mod-source-of-truth"); + + private final String propName; + + Metadata(String propName) { + this.propName = propName; + } + + public String propertyName() { + return propName; + } + + public static boolean isProperty(String property) { + for (Metadata meta : Metadata.values()) { + if (meta.propName.equals(property)) { + return true; + } + } + return false; + } + } + + + public static Map<String, Object> resolveCollectionfilter(String version, String type, + Map<String, String> filter) + throws CrudException { + + DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version); + + Map<String, Object> result = new HashMap<String, Object>(); + if (jaxbContext == null) { + throw new CrudException("", Status.NOT_FOUND); + } + final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL + .to(CaseFormat.UPPER_CAMEL, + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type))); + + for (String key : filter.keySet()) { + String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, key); + if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) != null) { + try { + DatabaseMapping mapping = modelObjectType.getDescriptor() + .getMappingForAttributeName(keyJavaName); + Object value = CrudServiceUtil.validateFieldType(filter.get(key), + mapping.getField().getType()); + result.put(key, value); + } catch (Exception ex) { + // Skip any exceptions thrown while validating the filter + // key value + continue; + } + } + } + + return result; + + } + + public static String resolveCollectionType(String version, String type) throws CrudException { + + DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version); + + if (jaxbContext == null) { + throw new CrudException("", Status.NOT_FOUND); + } + // Determine if the Object part is a collection type in the model + // definition + final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL + .to(CaseFormat.UPPER_CAMEL, + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type))); + + if (modelObjectType == null) { + throw new CrudException("", Status.NOT_FOUND); + } + + if (modelObjectType.getDescriptor().getMappings().size() == 1 + && modelObjectType.getDescriptor().getMappings().get(0).isCollectionMapping()) { + String childJavaObjectName = modelObjectType.getDescriptor().getMappings() + .get(0).getAttributeName(); + childJavaObjectName = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, childJavaObjectName); + final DynamicType childObjectType = jaxbContext.getDynamicType(childJavaObjectName); + if (childObjectType == null) { + // Should not happen as child object is defined in oxm model + // itself + throw new CrudException("", Status.NOT_FOUND); + } + return childObjectType.getDescriptor().getTableName(); + } else { + return modelObjectType.getDescriptor().getTableName(); + } + + } + + + public static Vertex validateIncomingUpsertPayload(String id, String version, String type, + JsonElement properties) + throws CrudException { + + try { + type = resolveCollectionType(version, type); + DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version); + String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)); + + final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass); + + Set<Map.Entry<String, JsonElement>> payloadEntriesSet = properties.getAsJsonObject() + .entrySet(); + + //loop through input to validate against schema + for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) { + String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey()); + + // check for valid field + if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) { + throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST); + } + + } + + Map<String, JsonElement> entriesMap = new HashMap<String, JsonElement>(); + for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) { + entriesMap.put(entry.getKey(), entry.getValue()); + } + + Vertex.Builder modelVertexBuilder = new Vertex.Builder(type); + if (id != null) { + modelVertexBuilder.id(id); + } + for (DatabaseMapping mapping : modelObjectType.getDescriptor().getMappings()) { + if (mapping.isAbstractDirectMapping()) { + DatabaseField field = mapping.getField(); + String defaultValue = mapping.getProperties().get("defaultValue") == null ? "" + : mapping.getProperties().get("defaultValue").toString(); + + String keyName = field.getName().substring(0, field.getName().indexOf("/")); + + if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName) + && !defaultValue.isEmpty()) { + modelVertexBuilder.property(keyName, + CrudServiceUtil.validateFieldType(defaultValue, field.getType())); + } + // if schema field is required and not set then reject + if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName) + && defaultValue.isEmpty()) { + throw new CrudException("Missing required field: " + keyName, Status.BAD_REQUEST); + } + // If invalid field then reject + if (entriesMap.containsKey(keyName)) { + Object value = CrudServiceUtil.validateFieldType(entriesMap.get(keyName) + .getAsString(), field.getType()); + modelVertexBuilder.property(keyName, value); + } + + // Set defaults + if (!defaultValue.isEmpty() && !entriesMap.containsKey(keyName)) { + modelVertexBuilder.property(keyName, + CrudServiceUtil.validateFieldType(defaultValue, field.getType())); + } + } + } + + return modelVertexBuilder.build(); + } catch (Exception e) { + throw new CrudException(e.getMessage(), Status.BAD_REQUEST); + } + } + + public static Vertex validateIncomingPatchPayload(String id, String version, String type, + JsonElement properties, Vertex existingVertex) + throws CrudException { + + try { + type = resolveCollectionType(version, type); + DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version); + String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)); + + final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass); + + Set<Map.Entry<String, JsonElement>> payloadEntriesSet = properties.getAsJsonObject() + .entrySet(); + + // Loop through the payload properties and merge with existing + // vertex props + for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) { + + String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey()); + + // check for valid field + if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) { + throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST); + } + + DatabaseField field = modelObjectType.getDescriptor() + .getMappingForAttributeName(keyJavaName).getField(); + String defaultValue = modelObjectType.getDescriptor() + .getMappingForAttributeName(keyJavaName) + .getProperties().get("defaultValue") == null ? "" + : modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) + .getProperties().get("defaultValue").toString(); + + // check if mandatory field is not set to null + if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull + && !defaultValue.isEmpty()) { + existingVertex.getProperties().put(entry.getKey(), + CrudServiceUtil.validateFieldType(defaultValue, field.getType())); + } else if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull + && defaultValue.isEmpty()) { + throw new CrudException("Mandatory field: " + entry.getKey() + + " can't be set to null", + Status.BAD_REQUEST); + } else if (!((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull + && existingVertex.getProperties().containsKey(entry.getKey())) { + existingVertex.getProperties().remove(entry.getKey()); + } else if (!(entry.getValue() instanceof JsonNull)) { + // add/update the value if found in existing vertex + Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(), + field.getType()); + existingVertex.getProperties().put(entry.getKey(), value); + } + + } + + return existingVertex; + } catch (Exception e) { + throw new CrudException(e.getMessage(), Status.BAD_REQUEST); + } + + } + + private static DatabaseField getDatabaseField(String fieldName, DynamicType modelObjectType) { + for (DatabaseField field : modelObjectType.getDescriptor().getAllFields()) { + int ix = field.getName().indexOf("/"); + if (ix <= 0) { + ix = field.getName().length(); + } + + String keyName = field.getName().substring(0, ix); + if (fieldName.equals(keyName)) { + return field; + } + } + return null; + } + + public static Vertex validateOutgoingPayload(String version, Vertex vertex) { + + Vertex.Builder modelVertexBuilder = new Vertex.Builder(vertex.getType()) + .id(vertex.getId().get()); + + try { + DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version); + String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, + vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()) != null + ? vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()).toString() : vertex.getType())); + final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass); + + for (String key : vertex.getProperties().keySet()) { + DatabaseField field = getDatabaseField(key, modelObjectType); + if (field != null) { + if (!Metadata.isProperty(key)) { + modelVertexBuilder.property(key, vertex.getProperties().get(key)); + } + } + } + return modelVertexBuilder.build(); + } catch (Exception ex) { + return vertex; + } + + } + + +} diff --git a/src/main/java/org/openecomp/schema/RelationshipSchema.java b/src/main/java/org/openecomp/schema/RelationshipSchema.java new file mode 100644 index 0000000..5b28e28 --- /dev/null +++ b/src/main/java/org/openecomp/schema/RelationshipSchema.java @@ -0,0 +1,138 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.schema; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import org.openecomp.crud.exception.CrudException; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.ws.rs.core.Response.Status; + +public class RelationshipSchema { + private static final Gson gson = new GsonBuilder().create(); + + public static final String SCHEMA_SOURCE_NODE_TYPE = "source-node-type"; + public static final String SCHEMA_TARGET_NODE_TYPE = "target-node-type"; + public static final String SCHEMA_RELATIONSHIP_TYPE = "relationship-type"; + public static final String SCHEMA_RELATIONSHIP_TYPES_ARRAY = "relationship-types"; + public static final String SCHEMA_RELATIONSHIP_PROPERTIES = "properties"; + public static final String SCHEMA_RELATIONS_ARRAY = "relations"; + + /** + * key = source-node-type:target-node-type:relationship-type value = map of properties with name + * and type . Like propertyName:PropertyType + */ + private HashMap<String, HashMap<String, Class<?>>> relations + = new HashMap<String, HashMap<String, Class<?>>>(); + /** + * Hashmap of valid relationship types alongwith properrties. + */ + private HashMap<String, HashMap<String, Class<?>>> relationTypes + = new HashMap<String, HashMap<String, Class<?>>>(); + + + public RelationshipSchema(String json) throws CrudException { + + JsonParser parser = new JsonParser(); + try { + JsonObject root = parser.parse(json).getAsJsonObject(); + JsonArray relationshipTypesArray = root.getAsJsonArray(SCHEMA_RELATIONSHIP_TYPES_ARRAY); + JsonArray relationsArray = root.getAsJsonArray(SCHEMA_RELATIONS_ARRAY); + + //First load all the relationship-types + for (JsonElement item : relationshipTypesArray) { + JsonObject obj = item.getAsJsonObject(); + String type = obj.get(SCHEMA_RELATIONSHIP_TYPE).getAsString(); + + + HashMap<String, Class<?>> props = new HashMap<String, Class<?>>(); + Set<Map.Entry<String, JsonElement>> entries = obj.get(SCHEMA_RELATIONSHIP_PROPERTIES) + .getAsJsonObject().entrySet(); + + for (Map.Entry<String, JsonElement> entry : entries) { + props.put(entry.getKey(), resolveClass(entry.getValue().getAsString())); + + } + relationTypes.put(type, props); + + } + + for (JsonElement item : relationsArray) { + JsonObject obj = item.getAsJsonObject(); + // Parse the Source/Taget nodeTypes + + String relationType = obj.get(SCHEMA_RELATIONSHIP_TYPE).getAsString(); + String key = obj.get(SCHEMA_SOURCE_NODE_TYPE).getAsString() + ":" + + obj.get(SCHEMA_TARGET_NODE_TYPE).getAsString() + ":" + relationType; + + + if (!relationTypes.containsKey(relationType)) { + throw new CrudException(SCHEMA_RELATIONSHIP_TYPE + ": " + relationType + " not found", + Status.BAD_REQUEST); + } + + relations.put(key, relationTypes.get(relationType)); + } + } catch (Exception e) { + throw new CrudException(e.getMessage(), Status.BAD_REQUEST); + } + + } + + + public HashMap<String, Class<?>> lookupRelation(String key) { + return this.relations.get(key); + } + + public HashMap<String, Class<?>> lookupRelationType(String type) { + return this.relationTypes.get(type); + } + + public boolean isValidType(String type) { + return relationTypes.containsKey(type); + } + + private Class<?> resolveClass(String type) throws CrudException, ClassNotFoundException { + Class<?> clazz = Class.forName(type); + validateClassTypes(clazz); + return clazz; + } + + private void validateClassTypes(Class<?> clazz) throws CrudException { + if (!clazz.isAssignableFrom(Integer.class) && !clazz.isAssignableFrom(Double.class) + && !clazz.isAssignableFrom(Boolean.class) && !clazz.isAssignableFrom(String.class)) { + throw new CrudException("", Status.BAD_REQUEST); + } + } + + +} diff --git a/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java b/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java new file mode 100644 index 0000000..51b33d0 --- /dev/null +++ b/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java @@ -0,0 +1,158 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.schema; + +import org.apache.commons.io.IOUtils; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.logging.CrudServiceMsgs; +import org.openecomp.crud.util.CrudServiceConstants; +import org.openecomp.crud.util.FileWatcher; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedSet; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.ws.rs.core.Response.Status; +import javax.xml.bind.JAXBException; + + +public class RelationshipSchemaLoader { + + private static Map<String, RelationshipSchema> versionContextMap + = new ConcurrentHashMap<String, RelationshipSchema>(); + private static SortedSet<Integer> versions = new TreeSet<Integer>(); + private static Map<String, Timer> timers = new ConcurrentHashMap<String, Timer>(); + + final static Pattern filePattern = Pattern.compile("aai_relationship_(.*).json"); + + + private static org.openecomp.cl.api.Logger logger = LoggerFactory.getInstance() + .getLogger(RelationshipSchemaLoader.class.getName()); + + public synchronized static void loadModels() { + + File[] listOfFiles = new File(CrudServiceConstants.CRD_HOME_MODEL).listFiles(); + + if (listOfFiles != null) { + for (File file : listOfFiles) { + if (file.isFile()) { + Matcher matcher = filePattern.matcher(file.getName()); + if (matcher.matches()) { + try { + RelationshipSchemaLoader.loadModel(matcher.group(1), file); + } catch (Exception e) { + logger.error(CrudServiceMsgs.INVALID_OXM_FILE, file.getName(), e.getMessage()); + } + } + + } + } + } else { + logger.error(CrudServiceMsgs.INVALID_OXM_DIR, CrudServiceConstants.CRD_HOME_MODEL); + } + + + } + + private static void addtimer(String version, File file) { + TimerTask task = null; + task = new FileWatcher( + file) { + protected void onChange(File file) { + // here we implement the onChange + logger.info(CrudServiceMsgs.OXM_FILE_CHANGED, file.getName()); + + try { + RelationshipSchemaLoader.loadModel(version, file); + } catch (Exception e) { + e.printStackTrace(); + } + + } + }; + + if (!timers.containsKey(version)) { + Timer timer = new Timer("aai_relationship_" + version); + timer.schedule(task, new Date(), 10000); + timers.put(version, timer); + + } + } + + private synchronized static void loadModel(String version, File file) + throws JAXBException, IOException, CrudException { + + InputStream inputStream = new FileInputStream(file); + String content = IOUtils.toString(inputStream, "UTF-8"); + versionContextMap.put(version, new RelationshipSchema(content)); + addtimer(version, file); + versions.add(Integer.parseInt(version.substring(1))); + } + + public static RelationshipSchema getSchemaForVersion(String version) throws CrudException { + if (versionContextMap == null || versionContextMap.isEmpty()) { + loadModels(); + } else if (!versionContextMap.containsKey(version)) { + try { + loadModel(version, new File(CrudServiceConstants.CRD_HOME_MODEL + "aai_relationship_" + + version + ".json")); + } catch (Exception e) { + throw new CrudException("", Status.NOT_FOUND); + } + } + + return versionContextMap.get(version); + } + + public static String getLatestSchemaVersion() throws CrudException { + return "v" + versions.last(); + } + + public static Map<String, RelationshipSchema> getVersionContextMap() { + return versionContextMap; + } + + public static void setVersionContextMap(HashMap<String, RelationshipSchema> versionContextMap) { + RelationshipSchemaLoader.versionContextMap = versionContextMap; + } + + public static void main(String[] args) throws FileNotFoundException, Exception { + File initialFile = new File("C:\\Software\\gizmo\\src\\main\\java\\org\\openecomp\\schema\\vio.json"); + + loadModel("v8", initialFile); + } + +} diff --git a/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java b/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java new file mode 100644 index 0000000..552b60a --- /dev/null +++ b/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java @@ -0,0 +1,341 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.schema; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.service.EdgePayload; +import org.openecomp.crud.util.CrudServiceUtil; +import org.radeox.util.logging.Logger; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.ws.rs.core.Response.Status; + +public class RelationshipSchemaValidator { + + public static final String SOURCE_NODE = "source"; + public static final String TARGET_NODE = "target"; + + final static Pattern urlPattern = Pattern.compile("services/inventory/(.*)/(.*)/(.*)"); + + public static Map<String, Object> resolveCollectionfilter(String version, String type, + Map<String, String> filter) + throws CrudException { + + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + if (schema == null) { + throw new CrudException("", Status.NOT_FOUND); + } + + HashMap<String, Class<?>> props = schema.lookupRelationType(type); + Map<String, Object> result = new HashMap<String, Object>(); + + for (String key : filter.keySet()) { + + if (props.containsKey(key)) { + try { + Object value = CrudServiceUtil.validateFieldType(filter.get(key), props.get(key)); + result.put(key, value); + } catch (Exception ex) { + // Skip any exceptions thrown while validating the filter + // key value + continue; + } + } + } + + return result; + + } + + public static void validateType(String version, String type) throws CrudException { + + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + if (!schema.isValidType(type)) { + throw new CrudException("Invalid " + RelationshipSchema.SCHEMA_RELATIONSHIP_TYPE + + ": " + type, + Status.BAD_REQUEST); + } + + } + + public static Edge validateIncomingAddPayload(String version, String type, Vertex sourceNode, + Vertex targetNode, JsonElement properties) + throws CrudException { + EdgePayload payload = new EdgePayload(); + payload.setSource("services/inventory/" + version + "/" + sourceNode.getType() + + "/" + sourceNode.getId().get()); + payload.setTarget("services/inventory/" + version + "/" + targetNode.getType() + + "/" + targetNode.getId().get()); + payload.setType(type); + payload.setProperties(properties); + return validateIncomingAddPayload(version, type, payload); + } + + public static Edge validateIncomingAddPayload(String version, String type, EdgePayload payload) + throws CrudException { + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + + try { + + if (payload.getSource() == null || payload.getTarget() == null) { + throw new CrudException("Source/Target not specified", Status.BAD_REQUEST); + } + + Matcher sourceMatcher = urlPattern.matcher(payload.getSource()); + Matcher targetMatcher = urlPattern.matcher(payload.getTarget()); + + if (!sourceMatcher.matches() || !targetMatcher.matches()) { + throw new CrudException("Invalid Source/Target Urls", Status.BAD_REQUEST); + } + + // create key based on source:target:relationshipType + String sourceNodeType = sourceMatcher.group(2); + String targetNodeType = targetMatcher.group(2); + + String sourceNodeId = sourceMatcher.group(3); + String targetNodeId = targetMatcher.group(3); + + String key = sourceNodeType + ":" + targetNodeType + ":" + type; + + // find the validate the key from the schema + HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key); + + if (schemaObject == null) { + throw new CrudException("Invalid source/target/relationship type: " + key, + Status.BAD_REQUEST); + } + + Edge.Builder modelEdgeBuilder = new Edge.Builder(type); + + modelEdgeBuilder.source(new Vertex.Builder(sourceNodeType).id(sourceNodeId).build()); + modelEdgeBuilder.target(new Vertex.Builder(targetNodeType).id(targetNodeId).build()); + + // validate it properties + validateEdgeProps(modelEdgeBuilder, payload.getProperties(), schemaObject); + + return modelEdgeBuilder.build(); + } catch (Exception ex) { + + throw new CrudException(ex.getMessage(), Status.BAD_REQUEST); + } + + } + + public static Edge validateIncomingPatchPayload(Edge edge, String version, EdgePayload payload) + throws CrudException { + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + + try { + if (payload.getSource() != null) { + Matcher sourceMatcher = urlPattern.matcher(payload.getSource()); + + if (!sourceMatcher.matches()) { + throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST); + } + String sourceNodeId = sourceMatcher.group(3); + if (!sourceNodeId.equals(edge.getSource().getId().get())) { + throw new CrudException("Source can't be updated", Status.BAD_REQUEST); + } + } + + if (payload.getTarget() != null) { + Matcher targetMatcher = urlPattern.matcher(payload.getTarget()); + + if (!targetMatcher.matches()) { + throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST); + } + String sourceNodeId = targetMatcher.group(3); + if (!sourceNodeId.equals(edge.getTarget().getId().get())) { + throw new CrudException("Target can't be updated", Status.BAD_REQUEST); + } + } + // create key based on source:target:relationshipType + + String key = edge.getSource().getType() + ":" + edge.getTarget().getType() + + ":" + edge.getType(); + + // find the validate the key from the schema + HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key); + + if (schemaObject == null) { + Logger.warn("key :" + key + + " not found in relationship schema . Skipping the schema validation"); + return edge; + } + + Set<Map.Entry<String, JsonElement>> entries = payload.getProperties() + .getAsJsonObject().entrySet(); + + for (Map.Entry<String, JsonElement> entry : entries) { + + if (!schemaObject.containsKey(entry.getKey())) { + throw new CrudException("Invalid property: " + entry.getKey(), Status.BAD_REQUEST); + } else if (entry.getValue() instanceof JsonNull && edge.getProperties() + .containsKey(entry.getKey())) { + edge.getProperties().remove(entry.getKey()); + } else if (!(entry.getValue() instanceof JsonNull)) { + Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(), + schemaObject.get(entry.getKey())); + edge.getProperties().put(entry.getKey(), value); + } + + } + + return edge; + + } catch (Exception ex) { + + throw new CrudException(ex.getMessage(), Status.BAD_REQUEST); + } + } + + public static Edge validateIncomingUpdatePayload(Edge edge, String version, Vertex sourceNode, + Vertex targetNode, JsonElement properties) + throws CrudException { + EdgePayload payload = new EdgePayload(); + payload.setSource("services/inventory/" + version + "/" + sourceNode.getType() + + "/" + sourceNode.getId().get()); + payload.setTarget("services/inventory/" + version + "/" + targetNode.getType() + + "/" + targetNode.getId().get()); + payload.setType(edge.getType()); + payload.setProperties(properties); + return validateIncomingUpdatePayload(edge, version, payload); + } + + public static Edge validateIncomingUpdatePayload(Edge edge, String version, EdgePayload payload) + throws CrudException { + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + + try { + + if (payload.getSource() != null) { + Matcher sourceMatcher = urlPattern.matcher(payload.getSource()); + + if (!sourceMatcher.matches()) { + throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST); + } + String sourceNodeId = sourceMatcher.group(3); + if (!sourceNodeId.equals(edge.getSource().getId().get())) { + throw new CrudException("Source can't be updated", Status.BAD_REQUEST); + } + } + + if (payload.getTarget() != null) { + Matcher targetMatcher = urlPattern.matcher(payload.getTarget()); + + if (!targetMatcher.matches()) { + throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST); + } + String sourceNodeId = targetMatcher.group(3); + if (!sourceNodeId.equals(edge.getTarget().getId().get())) { + throw new CrudException("Target can't be updated", Status.BAD_REQUEST); + } + } + // create key based on source:target:relationshipType + + String key = edge.getSource().getType() + ":" + edge.getTarget().getType() + + ":" + edge.getType(); + + // find the validate the key from the schema + HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key); + + if (schemaObject == null) { + Logger.warn("key :" + key + + " not found in relationship schema . Skipping the schema validation"); + return edge; + } + + Edge.Builder updatedEdgeBuilder = new Edge.Builder(edge.getType()).id(edge.getId().get()); + + updatedEdgeBuilder + .source(new Vertex.Builder(edge.getSource().getType()).id(edge.getSource().getId() + .get()).build()); + updatedEdgeBuilder + .target(new Vertex.Builder(edge.getTarget().getType()).id(edge.getTarget().getId() + .get()).build()); + + validateEdgeProps(updatedEdgeBuilder, payload.getProperties(), schemaObject); + + return updatedEdgeBuilder.build(); + } catch (Exception ex) { + + throw new CrudException(ex.getMessage(), Status.BAD_REQUEST); + } + } + + + private static void validateEdgeProps(Edge.Builder builder, JsonElement props, + HashMap<String, Class<?>> schemaObject) + throws CrudException { + Set<Map.Entry<String, JsonElement>> entries = props.getAsJsonObject().entrySet(); + + for (Map.Entry<String, JsonElement> entry : entries) { + + if (!schemaObject.containsKey(entry.getKey())) { + throw new CrudException("Invalid property: " + entry.getKey(), Status.BAD_REQUEST); + } else { + Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(), + schemaObject.get(entry.getKey())); + builder.property(entry.getKey(), value); + } + + } + + } + + public static Edge validateOutgoingPayload(String version, Edge edge) throws CrudException { + + Edge.Builder modelEdgeBuilder = new Edge.Builder(edge.getType()).id(edge.getId() + .get()).source(edge.getSource()) + .target(edge.getTarget()); + + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + + String key = edge.getSource().getType() + ":" + edge.getTarget().getType() + + ":" + edge.getType(); + HashMap<String, Class<?>> schemaObject = schema.lookupRelation(key); + + if (schemaObject == null || schemaObject.isEmpty()) { + return edge; + } + + for (String prop : edge.getProperties().keySet()) { + if (schemaObject.containsKey(prop)) { + modelEdgeBuilder.property(prop, edge.getProperties().get(prop)); + } + + } + return modelEdgeBuilder.build(); + } + +} |