package org.onap.champ.service; import org.onap.aai.champcore.ChampGraph; import org.onap.aai.champcore.ChampTransaction; import org.onap.aai.champcore.exceptions.ChampMarshallingException; import org.onap.aai.champcore.exceptions.ChampObjectNotExistsException; import org.onap.aai.champcore.exceptions.ChampRelationshipNotExistsException; import org.onap.aai.champcore.exceptions.ChampSchemaViolationException; import org.onap.aai.champcore.exceptions.ChampTransactionException; import org.onap.aai.champcore.exceptions.ChampUnmarshallingException; import org.onap.aai.champcore.model.ChampElement; import org.onap.aai.champcore.model.ChampObject; import org.onap.aai.champcore.model.ChampRelationship; import org.onap.aai.champcore.model.fluent.object.ObjectBuildOrPropertiesStep; import org.onap.aai.cl.api.Logger; import org.onap.aai.cl.eelf.LoggerFactory; import org.onap.champ.exception.ChampServiceException; import org.onap.champ.service.logging.ChampMsgs; import org.onap.champ.util.ChampProperties; import org.onap.champ.util.ChampServiceConstants; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.ws.rs.core.Response.Status; public class ChampDataService { private ChampUUIDService champUUIDService; private ChampGraph graphImpl; private ChampTransactionCache cache; private static final String KEY_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_KEY_NAME); private static final String SOT_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_SOT_NAME); private static final String CREATED_TS_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_CREATED_TS_NAME); private static final String LAST_MOD_TS_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_LAST_MOD_TS_NAME); private Logger logger = LoggerFactory.getInstance().getLogger(ChampDataService.class); public ChampDataService(ChampUUIDService champUUIDService, ChampGraph graphImpl, ChampTransactionCache cache) { this.champUUIDService = champUUIDService; this.graphImpl = graphImpl; this.cache = cache; } public ChampObject getObject(String id, Optional transaction) throws ChampServiceException { Optional retrieved = Optional.empty(); try { retrieved = champUUIDService.getObjectbyUUID(id, transaction.orElse(null)); } catch (ChampUnmarshallingException | ChampTransactionException e) { throw new ChampServiceException("Error: " + e.getMessage(), Status.INTERNAL_SERVER_ERROR); } if (retrieved.isPresent()) { return (ChampObject) champUUIDService.populateUUIDKey(retrieved.get()); } else { return null; } } public ChampObject storeObject(ChampObject object, Optional transaction) throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException, ChampTransactionException, ChampServiceException { if (object.getProperty(KEY_NAME).isPresent() || object.getKey().isPresent()) { throw new ChampServiceException(KEY_NAME + " can't be updated", Status.BAD_REQUEST); } champUUIDService.populateUUIDProperty(object, java.util.UUID.randomUUID().toString()); addTimestamps(object, null); ChampObject created = graphImpl.storeObject(object, transaction); return (ChampObject) champUUIDService.populateUUIDKey(created); } public ChampObject replaceObject(ChampObject object, String objectId, Optional transaction) throws ChampServiceException, ChampUnmarshallingException, ChampTransactionException, ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException { if (object.getKey().isPresent() && (!object.getKeyValue().equals(objectId))) { throw new ChampServiceException("Object Id in the URI doesn't match the body.", Status.BAD_REQUEST); } if (object.getProperty(KEY_NAME).isPresent() && !object.getProperty(KEY_NAME).get().toString().equals(objectId)) { throw new ChampServiceException(KEY_NAME + " can't be updated", Status.BAD_REQUEST); } Optional retrieved = champUUIDService.getObjectbyUUID(objectId, transaction.orElse(null)); if (!retrieved.isPresent()) { throw new ChampServiceException(objectId + " not found", Status.NOT_FOUND); } ObjectBuildOrPropertiesStep payloadBuilder = ChampObject.create().from(object).withKey(retrieved.get().getKey().get()) .withProperty(KEY_NAME, objectId); if (retrieved.get().getProperty(SOT_NAME).isPresent()){ payloadBuilder = payloadBuilder.withProperty(SOT_NAME, retrieved.get().getProperty(SOT_NAME).get()); } if (object.getProperty(CREATED_TS_NAME).isPresent() && retrieved.get().getProperty(CREATED_TS_NAME).isPresent()) { // the timestamps in object are parsed as strings regardless of how the input json is. Convert retrieved to string for easy comparison if (!retrieved.get().getProperty(CREATED_TS_NAME).get().toString().equals(object.getProperty(CREATED_TS_NAME).get())) { throw new ChampServiceException(CREATED_TS_NAME + " can't be updated", Status.BAD_REQUEST); } } if (object.getProperty(LAST_MOD_TS_NAME).isPresent() && retrieved.get().getProperty(LAST_MOD_TS_NAME).isPresent()) { if (!retrieved.get().getProperty(LAST_MOD_TS_NAME).get().toString().equals(object.getProperty(LAST_MOD_TS_NAME).get())) { throw new ChampServiceException(LAST_MOD_TS_NAME + " can't be updated", Status.BAD_REQUEST); } } ChampObject payload = payloadBuilder.build(); addTimestamps(payload, (Long)retrieved.get().getProperty(CREATED_TS_NAME).orElse(null)); ChampObject updated = graphImpl.replaceObject(payload, transaction); return (ChampObject) champUUIDService.populateUUIDKey(updated); } public void deleteObject(String objectId, Optional transaction) throws ChampServiceException, ChampObjectNotExistsException, ChampTransactionException, ChampUnmarshallingException { Optional retrieved = champUUIDService.getObjectbyUUID(objectId, transaction.orElse(null)); if (!retrieved.isPresent()) { throw new ChampServiceException(objectId + " not found", Status.NOT_FOUND); } Stream relationships = graphImpl.retrieveRelationships(retrieved.get(), transaction); if (relationships.count() > 0) { throw new ChampServiceException("Attempt to delete vertex with id " + objectId + " which has incident edges.", Status.BAD_REQUEST); } graphImpl.deleteObject(retrieved.get().getKey().get(), transaction); } public ChampRelationship storeRelationship(ChampRelationship r, Optional transaction) throws ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException, ChampRelationshipNotExistsException, ChampUnmarshallingException, ChampTransactionException, ChampServiceException { if (r.getSource() == null || !r.getSource().getKey().isPresent() || r.getTarget() == null || !r.getTarget().getKey().isPresent()) { logger.error(ChampMsgs.CHAMP_DATA_SERVICE_ERROR, "Source/Target Object key must be provided"); throw new ChampServiceException("Source/Target Object key must be provided", Status.BAD_REQUEST); } if (r.getProperty(KEY_NAME).isPresent() || r.getKey().isPresent()) { logger.error(ChampMsgs.CHAMP_DATA_SERVICE_ERROR, "key or " + KEY_NAME + " not allowed while creating new Objects"); throw new ChampServiceException("key or " + KEY_NAME + " not allowed while creating new Objects", Status.BAD_REQUEST); } Optional source = champUUIDService.getObjectbyUUID(r.getSource().getKey().get().toString(), transaction.orElse(null)); Optional target = champUUIDService.getObjectbyUUID(r.getTarget().getKey().get().toString(), transaction.orElse(null)); if (!source.isPresent() || !target.isPresent()) { logger.error(ChampMsgs.CHAMP_DATA_SERVICE_ERROR, "Source/Target object not found"); throw new ChampServiceException("Source/Target object not found", Status.BAD_REQUEST); } champUUIDService.populateUUIDProperty(r, java.util.UUID.randomUUID().toString()); ChampRelationship payload = new ChampRelationship.Builder(source.get(), target.get(), r.getType()) .properties(r.getProperties()).build(); addTimestamps(payload, null); ChampRelationship created = graphImpl.storeRelationship(payload, transaction); return (ChampRelationship) champUUIDService.populateUUIDKey(created); } public ChampRelationship updateRelationship(ChampRelationship r, String rId, Optional transaction) throws ChampServiceException, ChampUnmarshallingException, ChampTransactionException, ChampMarshallingException, ChampSchemaViolationException, ChampRelationshipNotExistsException { if (r.getKey().isPresent() && (!r.getKeyValue().equals(rId))) { throw new ChampServiceException("Relationship Id in the URI \"" + rId + "\" doesn't match the URI in the body" + " \"" + r.getKeyValue() + "\"", Status.BAD_REQUEST); } if (r.getProperty(KEY_NAME).isPresent() && !r.getProperty(KEY_NAME).get().toString().equals(rId)) { throw new ChampServiceException(KEY_NAME + " can't be updated", Status.BAD_REQUEST); } Optional retrieved = champUUIDService.getRelationshipbyUUID(rId, transaction.orElse(null)); if (!retrieved.isPresent()) { throw new ChampServiceException(rId + " not found", Status.NOT_FOUND); } // check if key is present or if it equals the key that is in the URI if (r.getSource() == null || !r.getSource().getKey().isPresent() || r.getTarget() == null || !r.getTarget().getKey().isPresent()) { throw new ChampServiceException("Source/Target Object key must be provided", Status.BAD_REQUEST); } ChampObject source = retrieved.get().getSource(); ChampObject target = retrieved.get().getTarget(); if (!source.getProperty(KEY_NAME).get().toString().equals(r.getSource().getKey().get().toString()) || !target.getProperty(KEY_NAME).get().toString().equals(r.getTarget().getKey().get().toString())) { throw new ChampServiceException("Source/Target cannot be updated", Status.BAD_REQUEST); } if (r.getProperty(CREATED_TS_NAME).isPresent() && retrieved.get().getProperty(CREATED_TS_NAME).isPresent()) { if (!retrieved.get().getProperty(CREATED_TS_NAME).get().toString().equals(r.getProperty(CREATED_TS_NAME).get())) { throw new ChampServiceException(CREATED_TS_NAME + " can't be updated", Status.BAD_REQUEST); } } if (r.getProperty(LAST_MOD_TS_NAME).isPresent() && retrieved.get().getProperty(LAST_MOD_TS_NAME).isPresent()) { if (!retrieved.get().getProperty(LAST_MOD_TS_NAME).get().toString().equals(r.getProperty(LAST_MOD_TS_NAME).get())) { throw new ChampServiceException(LAST_MOD_TS_NAME + " can't be updated", Status.BAD_REQUEST); } } ChampRelationship payload = new ChampRelationship.Builder(source, target, r.getType()) .key(retrieved.get().getKey().get()).properties(r.getProperties()).property(KEY_NAME, rId).build(); addTimestamps(payload, (Long)retrieved.get().getProperty(CREATED_TS_NAME).orElse(null)); ChampRelationship updated = graphImpl.replaceRelationship(payload, transaction); return (ChampRelationship) champUUIDService.populateUUIDKey(updated); } public void deleteRelationship(String relationshipId, Optional transaction) throws ChampServiceException, ChampRelationshipNotExistsException, ChampTransactionException, ChampUnmarshallingException { Optional retrieved = champUUIDService.getRelationshipbyUUID(relationshipId, transaction.orElse(null)); if (!retrieved.isPresent()) { throw new ChampServiceException(relationshipId + " not found", Status.NOT_FOUND); } graphImpl.deleteRelationship(retrieved.get(), transaction); } public List getRelationshipsByObject(String objectId, Optional transaction) throws ChampServiceException { try { Optional retrievedObject = champUUIDService.getObjectbyUUID(objectId, transaction.orElse(null)); if (!retrievedObject.isPresent()) { throw new ChampServiceException(objectId + " not found", Status.NOT_FOUND); } List relations = new ArrayList(); Stream retrieved = graphImpl.retrieveRelationships(retrievedObject.get(), transaction); relations = champUUIDService.populateUUIDKey(retrieved.collect(Collectors.toList())); return relations; } catch (ChampObjectNotExistsException e) { throw new ChampServiceException(" obj not found", Status.NOT_FOUND); } catch (ChampUnmarshallingException | ChampTransactionException e) { throw new ChampServiceException("Internal Error", Status.INTERNAL_SERVER_ERROR); } } /** * Gets the ChampObjects that pass filter * @param filter key/value pairs that must be present in the returned objects * @param properties properties that will show up in the object * @return * @throws ChampServiceException */ public List queryObjects(Map filter, HashSet properties) throws ChampServiceException { try { Stream retrieved = graphImpl.queryObjects(filter); List objects = champUUIDService.populateUUIDKey(retrieved.collect(Collectors.toList())); if (!properties.contains("all")) { for (ChampObject champObject : objects) { champObject.dropProperties(properties); } } return objects; } catch (ChampTransactionException e) { throw new ChampServiceException("Internal Error", Status.INTERNAL_SERVER_ERROR); } } public List queryRelationships(Map filter) throws ChampServiceException { try { List relations = new ArrayList(); Stream retrieved; retrieved = graphImpl.queryRelationships(filter); relations = champUUIDService.populateUUIDKey(retrieved.collect(Collectors.toList())); return relations; } catch (ChampTransactionException e) { throw new ChampServiceException("Internal Error", Status.INTERNAL_SERVER_ERROR); } } public ChampRelationship getRelationship(String id, Optional transaction) throws ChampServiceException { Optional retrieved = Optional.empty(); try { retrieved = champUUIDService.getRelationshipbyUUID(id, transaction.orElse(null)); } catch (ChampUnmarshallingException | ChampTransactionException e) { throw new ChampServiceException("Error: " + e.getMessage(), Status.INTERNAL_SERVER_ERROR); } if (retrieved.isPresent()) { return (ChampRelationship) champUUIDService.populateUUIDKey(retrieved.get()); } else { return null; } } public String openTransaction() { ChampTransaction transaction = graphImpl.openTransaction(); String transacId = transaction.id(); cache.put(transacId, transaction); return transacId; } public void commitTransaction(String tId) throws ChampServiceException, ChampTransactionException { ChampTransaction transaction = cache.get(tId); if (transaction == null) { throw new ChampServiceException("Transaction Not found: " + tId, Status.NOT_FOUND); } graphImpl.commitTransaction(transaction); cache.invalidate(tId); cache.invalidate(transaction.id()); } public void rollbackTransaction(String tId) throws ChampServiceException, ChampTransactionException { ChampTransaction transaction = cache.get(tId); if (transaction == null) { throw new ChampServiceException("Transaction Not found: " + tId, Status.NOT_FOUND); } graphImpl.rollbackTransaction(transaction); cache.invalidate(tId); cache.invalidate(transaction.id()); } public ChampTransaction getTransaction(String id) { return cache.get(id); } private void addTimestamps(ChampElement e, Long oldCreated) { Long timestamp = System.currentTimeMillis(); if (oldCreated == null) { e.getProperties().put(CREATED_TS_NAME, timestamp); } else { e.getProperties().put(CREATED_TS_NAME, oldCreated); } e.getProperties().put(LAST_MOD_TS_NAME, timestamp); } }