Extract HttpEntry notification logic into a separate NotificationService 14/138514/1
authorFiete Ostkamp <Fiete.Ostkamp@telekom.de>
Sat, 20 Jul 2024 08:44:22 +0000 (10:44 +0200)
committerFiete Ostkamp <Fiete.Ostkamp@telekom.de>
Sat, 20 Jul 2024 08:44:22 +0000 (10:44 +0200)
- introduce NotificationService
- rename ueb package to notification
- remove meaningless javadocs

Issue-ID: AAI-3930
Change-Id: Ic6ffd5511235d2400b0d6db71f7d7aa13d2b076b
Signed-off-by: Fiete Ostkamp <Fiete.Ostkamp@telekom.de>
19 files changed:
aai-core/src/main/java/org/onap/aai/config/IntrospectionConfig.java
aai-core/src/main/java/org/onap/aai/introspection/LoaderFactory.java
aai-core/src/main/java/org/onap/aai/prevalidation/ValidationService.java
aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java
aai-core/src/main/java/org/onap/aai/rest/notification/NotificationEvent.java [moved from aai-core/src/main/java/org/onap/aai/rest/ueb/NotificationEvent.java with 98% similarity]
aai-core/src/main/java/org/onap/aai/rest/notification/NotificationService.java [new file with mode: 0644]
aai-core/src/main/java/org/onap/aai/rest/notification/UEBNotification.java [moved from aai-core/src/main/java/org/onap/aai/rest/ueb/UEBNotification.java with 99% similarity]
aai-core/src/test/java/org/onap/aai/AAISetup.java
aai-core/src/test/java/org/onap/aai/DataLinkSetup.java
aai-core/src/test/java/org/onap/aai/HttpTestUtil.java
aai-core/src/test/java/org/onap/aai/introspection/sideeffect/DataLinkTest.java
aai-core/src/test/java/org/onap/aai/parsers/query/GraphTraversalTest.java
aai-core/src/test/java/org/onap/aai/query/builder/QueryBuilderTestAbstraction.java
aai-core/src/test/java/org/onap/aai/rest/ImpliedDeleteIntegrationTest.java
aai-core/src/test/java/org/onap/aai/rest/NotificationDmaapEventTest.java
aai-core/src/test/java/org/onap/aai/rest/db/HttpEntryTest.java
aai-core/src/test/java/org/onap/aai/rest/ueb/UEBNotificationTest.java
aai-core/src/test/java/org/onap/aai/serialization/db/DbSerializer_needsFakeRulesTest.java
aai-core/src/test/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngineTest.java

index 2a4673c..a9fcb88 100644 (file)
@@ -37,7 +37,6 @@ import org.springframework.context.annotation.Import;
 @Import({ConfigConfiguration.class, SchemaServiceConfiguration.class, NodesConfiguration.class,
         EdgesConfiguration.class})
 @Configuration
-
 public class IntrospectionConfig {
 
     private Map<SchemaVersion, MoxyLoader> moxyInstanceMap = new ConcurrentHashMap<>();
@@ -45,11 +44,6 @@ public class IntrospectionConfig {
     @Autowired
     NodesConfiguration nodesConfiguration;
 
-    @Bean
-    public LoaderFactory loaderFactory(SchemaVersions schemaVersions) {
-        return new LoaderFactory(moxyLoaderInstance(schemaVersions));
-    }
-
     @Bean
     public Map<SchemaVersion, MoxyLoader> moxyLoaderInstance(SchemaVersions schemaVersions) {
         for (SchemaVersion version : schemaVersions.getVersions()) {
index dd1bc0f..e35f5b9 100644 (file)
@@ -23,27 +23,24 @@ package org.onap.aai.introspection;
 import java.util.Map;
 
 import org.onap.aai.setup.SchemaVersion;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
 
+/**
+ * Factory method that grants access to the globally loaded schema versions.
+ * There is one {@link MoxyLoader} instance for each api version ({@link SchemaVersion}) that the AAI supports.
+ */
+@Component
 public class LoaderFactory {
 
-    @Autowired
-    public Map<SchemaVersion, MoxyLoader> moxyLoaderInstance;
+    private final Map<SchemaVersion, MoxyLoader> moxyLoaderInstance;
 
     public LoaderFactory(Map<SchemaVersion, MoxyLoader> moxyLoaderInstance) {
         this.moxyLoaderInstance = moxyLoaderInstance;
     }
 
     /**
-     * Creates a new Loader object.
-     *
-     * @param type
-     *        the type
-     * @param version
-     *        the version
-     * @param llBuilder
-     *        the ll builder
-     * @return the loader
+     * Contrary to the naming, this method does not create a new loader,
+     * but rather returns an existing loader instance
      */
     public Loader createLoaderForVersion(ModelType type, SchemaVersion version) {
 
@@ -52,7 +49,6 @@ public class LoaderFactory {
         }
 
         return null;
-
     }
 
     public Loader getLoaderStrategy(ModelType type, SchemaVersion version) {
@@ -61,15 +57,9 @@ public class LoaderFactory {
             return getMoxyLoaderInstance().get(version);
         }
         return null;
-
     }
 
     public Map<SchemaVersion, MoxyLoader> getMoxyLoaderInstance() {
         return moxyLoaderInstance;
     }
-
-    public void setMoxyLoaderInstance(Map<SchemaVersion, MoxyLoader> moxyLoaderInstance) {
-        this.moxyLoaderInstance = moxyLoaderInstance;
-    }
-
 }
index 939c838..093062a 100644 (file)
@@ -43,7 +43,7 @@ import javax.annotation.PostConstruct;
 import org.apache.http.conn.ConnectTimeoutException;
 import org.onap.aai.exceptions.AAIException;
 import org.onap.aai.introspection.Introspector;
-import org.onap.aai.rest.ueb.NotificationEvent;
+import org.onap.aai.rest.notification.NotificationEvent;
 import org.onap.aai.restclient.RestClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
index 85c8766..94dc63a 100644 (file)
@@ -54,7 +54,8 @@ import org.onap.aai.parsers.query.QueryParser;
 import org.onap.aai.prevalidation.ValidationService;
 import org.onap.aai.query.builder.QueryOptions;
 import org.onap.aai.query.entities.PaginationResult;
-import org.onap.aai.rest.ueb.UEBNotification;
+import org.onap.aai.rest.notification.NotificationService;
+import org.onap.aai.rest.notification.UEBNotification;
 import org.onap.aai.restcore.HttpMethod;
 import org.onap.aai.schema.enums.ObjectMetadata;
 import org.onap.aai.serialization.db.DBSerializer;
@@ -70,7 +71,6 @@ import org.onap.aai.setup.SchemaVersions;
 import org.onap.aai.transforms.XmlFormatTransformer;
 import org.onap.aai.util.AAIConfig;
 import org.onap.aai.util.AAIConstants;
-import org.onap.aai.util.delta.DeltaEvents;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -84,17 +84,11 @@ public class HttpEntry {
     private static final Logger LOGGER = LoggerFactory.getLogger(HttpEntry.class);
 
     private ModelType introspectorFactoryType;
-
     private QueryStyle queryStyle;
-
     private SchemaVersion version;
-
     private Loader loader;
-
     private TransactionalGraphEngine dbEngine;
 
-    private boolean processSingle = true;
-
     @Autowired
     private NodeIngestor nodeIngestor;
 
@@ -104,25 +98,17 @@ public class HttpEntry {
     @Autowired
     private SchemaVersions schemaVersions;
 
+    @Autowired
+    private NotificationService notificationService;
+
     @Value("${schema.uri.base.path}")
     private String basePath;
 
-    @Value("${delta.events.enabled:false}")
-    private boolean isDeltaEventsEnabled;
-
     private String serverBase;
 
     @Autowired
     private XmlFormatTransformer xmlFormatTransformer;
 
-    /**
-     * Inject the validation service if the profile pre-valiation is enabled,
-     * Otherwise this variable will be set to null and thats why required=false
-     * so that it can continue even if pre validation isn't enabled
-     */
-    @Autowired(required = false)
-    private ValidationService validationService;
-
     private UEBNotification notification;
 
     private int notificationDepth;
@@ -292,7 +278,7 @@ public class HttpEntry {
                     if (cleanUp == null) {
                         cleanUp = "false";
                     }
-                    if (vertices.size() > 1 && processSingle
+                    if (vertices.size() > 1
                             && !(method.equals(HttpMethod.GET) || method.equals(HttpMethod.GET_RELATIONSHIP))) {
                         if (method.equals(HttpMethod.DELETE)) {
 
@@ -539,7 +525,7 @@ public class HttpEntry {
                              */
 
                             if (isDelVerticesPresent) {
-                                this.buildNotificationEvent(sourceOfTruth, status, transactionId, notification,
+                                notificationService.buildNotificationEvent(sourceOfTruth, status, transactionId, notification,
                                         deleteObjects, uriMap, deleteRelatedObjects, basePath);
                             }
                             break;
@@ -624,7 +610,7 @@ public class HttpEntry {
         }
 
         if (success) {
-            generateEvents(sourceOfTruth, serializer, transactionId, queryEngine, mainVertexesToNotifyOn);
+            notificationService.generateEvents(notification, notificationDepth, sourceOfTruth, serializer, transactionId, queryEngine, mainVertexesToNotifyOn, version);
         } else {
             notification.clearEvents();
         }
@@ -655,104 +641,6 @@ public class HttpEntry {
             : query.getQueryBuilder().toPaginationResult(queryOptions.getPageable());
     }
 
-    /**
-     * Generate notification events for the resulting db requests.
-     */
-    private void generateEvents(String sourceOfTruth, DBSerializer serializer, String transactionId,
-            QueryEngine queryEngine, Set<Vertex> mainVertexesToNotifyOn) throws AAIException {
-        if (notificationDepth == AAIProperties.MINIMUM_DEPTH) {
-            serializer.getUpdatedVertexes().entrySet().stream().filter(Map.Entry::getValue).map(Map.Entry::getKey)
-                    .forEach(mainVertexesToNotifyOn::add);
-        }
-        Set<Vertex> edgeVertexes = serializer.touchStandardVertexPropertiesForEdges().stream()
-                .filter(v -> !mainVertexesToNotifyOn.contains(v)).collect(Collectors.toSet());
-        try {
-            createNotificationEvents(mainVertexesToNotifyOn, sourceOfTruth, serializer, transactionId, queryEngine,
-                    notificationDepth);
-            if ("true".equals(AAIConfig.get("aai.notification.both.sides.enabled", "true"))) {
-                createNotificationEvents(edgeVertexes, sourceOfTruth, serializer, transactionId, queryEngine,
-                        AAIProperties.MINIMUM_DEPTH);
-            }
-        } catch (UnsupportedEncodingException e) {
-            LOGGER.warn("Encountered exception generating events", e);
-        }
-
-        // Since @Autowired required is set to false, we need to do a null check
-        // for the existence of the validationService since its only enabled if profile is enabled
-        if (validationService != null) {
-            validationService.validate(notification.getEvents());
-        }
-        notification.triggerEvents();
-        if (isDeltaEventsEnabled) {
-            try {
-                DeltaEvents deltaEvents =
-                        new DeltaEvents(transactionId, sourceOfTruth, version.toString(), serializer.getObjectDeltas());
-                deltaEvents.triggerEvents();
-            } catch (Exception e) {
-                LOGGER.error("Error sending Delta Events", e);
-            }
-        }
-    }
-
-    /**
-     * Generate notification events for provided set of vertexes at the specified depth
-     */
-    private void createNotificationEvents(Set<Vertex> vertexesToNotifyOn, String sourceOfTruth, DBSerializer serializer,
-            String transactionId, QueryEngine queryEngine, int eventDepth)
-            throws AAIException, UnsupportedEncodingException {
-        for (Vertex vertex : vertexesToNotifyOn) {
-            if (canGenerateEvent(vertex)) {
-                boolean isCurVertexNew =
-                        vertex.value(AAIProperties.CREATED_TS).equals(vertex.value(AAIProperties.LAST_MOD_TS));
-                Status curObjStatus = (isCurVertexNew) ? Status.CREATED : Status.OK;
-
-                Introspector curObj = serializer.getLatestVersionView(vertex, eventDepth);
-                String aaiUri = vertex.<String>property(AAIProperties.AAI_URI).value();
-                String uri = String.format("%s/%s%s", basePath, version, aaiUri);
-                HashMap<String, Introspector> curRelatedObjs = new HashMap<>();
-                if (!curObj.isTopLevel()) {
-                    curRelatedObjs = serializer.getRelatedObjects(queryEngine, vertex, curObj, this.loader);
-                }
-                notification.createNotificationEvent(transactionId, sourceOfTruth, curObjStatus, URI.create(uri),
-                        curObj, curRelatedObjs, basePath);
-            }
-        }
-    }
-
-    /**
-     * Verifies that vertex has needed properties to generate on
-     *
-     * @param vertex Vertex to be verified
-     * @return <code>true</code> if vertex has necessary properties and exists
-     */
-    private boolean canGenerateEvent(Vertex vertex) {
-        boolean canGenerate = true;
-        try {
-            if (!vertex.property(AAIProperties.AAI_URI).isPresent()) {
-                LOGGER.debug("Encountered an vertex {} with missing aai-uri", vertex.id());
-                canGenerate = false;
-            } else if (!vertex.property(AAIProperties.CREATED_TS).isPresent()
-                    || !vertex.property(AAIProperties.LAST_MOD_TS).isPresent()) {
-                LOGGER.debug("Encountered an vertex {} with missing timestamp", vertex.id());
-                canGenerate = false;
-            }
-        } catch (IllegalStateException e) {
-            if (e.getMessage().contains(" was removed")) {
-                LOGGER.warn("Attempted to generate event for non existent vertex", e);
-            } else {
-                LOGGER.warn("Encountered exception generating events", e);
-            }
-            canGenerate = false;
-        }
-        return canGenerate;
-    }
-
-    /**
-     * Gets the media type.
-     *
-     * @param mediaTypeList the media type list
-     * @return the media type
-     */
     private String getMediaType(List<MediaType> mediaTypeList) {
         String mediaType = MediaType.APPLICATION_JSON; // json is the default
         for (MediaType mt : mediaTypeList) {
@@ -763,28 +651,6 @@ public class HttpEntry {
         return mediaType;
     }
 
-    /**
-     * Gets the object from db.
-     *
-     * @param serializer the serializer
-     * @param query the query
-     * @param obj the obj
-     * @param uri the uri
-     * @param depth the depth
-     * @param cleanUp the clean up
-     * @return the object from db
-     * @throws AAIException the AAI exception
-     * @throws IllegalAccessException the illegal access exception
-     * @throws IllegalArgumentException the illegal argument exception
-     * @throws InvocationTargetException the invocation target exception
-     * @throws SecurityException the security exception
-     * @throws InstantiationException the instantiation exception
-     * @throws NoSuchMethodException the no such method exception
-     * @throws UnsupportedEncodingException the unsupported encoding exception
-     * @throws MalformedURLException the malformed URL exception
-     * @throws AAIUnknownObjectException
-     * @throws URISyntaxException
-     */
     private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
             Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp)
             throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
@@ -801,29 +667,6 @@ public class HttpEntry {
 
     }
 
-    /**
-     * Gets the object from db.
-     *
-     * @param serializer the serializer
-     * @param query the query
-     * @param obj the obj
-     * @param uri the uri
-     * @param depth the depth
-     * @param cleanUp the clean up
-     * @param isSkipRelatedTo include related to flag
-     * @return the object from db
-     * @throws AAIException the AAI exception
-     * @throws IllegalAccessException the illegal access exception
-     * @throws IllegalArgumentException the illegal argument exception
-     * @throws InvocationTargetException the invocation target exception
-     * @throws SecurityException the security exception
-     * @throws InstantiationException the instantiation exception
-     * @throws NoSuchMethodException the no such method exception
-     * @throws UnsupportedEncodingException the unsupported encoding exception
-     * @throws MalformedURLException the malformed URL exception
-     * @throws AAIUnknownObjectException
-     * @throws URISyntaxException
-     */
     private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
             Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp, boolean isSkipRelatedTo)
             throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
@@ -840,25 +683,6 @@ public class HttpEntry {
 
     }
 
-    /**
-     * Gets the object from db.
-     *
-     * @param serializer the serializer
-     * @param query the query
-     * @param uri the uri
-     * @return the object from db
-     * @throws AAIException the AAI exception
-     * @throws IllegalAccessException the illegal access exception
-     * @throws IllegalArgumentException the illegal argument exception
-     * @throws InvocationTargetException the invocation target exception
-     * @throws SecurityException the security exception
-     * @throws InstantiationException the instantiation exception
-     * @throws NoSuchMethodException the no such method exception
-     * @throws UnsupportedEncodingException the unsupported encoding exception
-     * @throws MalformedURLException the malformed URL exception
-     * @throws AAIUnknownObjectException
-     * @throws URISyntaxException
-     */
     private Introspector getRelationshipObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query,
             URI uri, boolean isSkipRelatedTo) throws AAIException, IllegalArgumentException, SecurityException,
             UnsupportedEncodingException, AAIUnknownObjectException {
@@ -877,36 +701,15 @@ public class HttpEntry {
         return serializer.dbToRelationshipObject(v, isSkipRelatedTo);
     }
 
-    /**
-     * Creates the not found message.
-     *
-     * @param resultType the result type
-     * @param uri the uri
-     * @return the string
-     */
     private String createNotFoundMessage(String resultType, URI uri) {
         return "No Node of type " + resultType + " found at: " + uri.getPath();
     }
 
-    /**
-     * Creates the not found message.
-     *
-     * @param resultType the result type
-     * @param uri the uri
-     * @return the string
-     */
     private String createRelationshipNotFoundMessage(String resultType, URI uri) {
         return "No relationship found of type " + resultType + " at the given URI: " + uri.getPath()
                 + "/relationship-list";
     }
 
-    /**
-     * Sets the depth.
-     *
-     * @param depthParam the depth param
-     * @return the int
-     * @throws AAIException the AAI exception
-     */
     protected int setDepth(Introspector obj, String depthParam) throws AAIException {
         int depth = AAIProperties.MAXIMUM_DEPTH;
 
@@ -1003,25 +806,4 @@ public class HttpEntry {
         return relatedObjectsMap;
 
     }
-
-    private void buildNotificationEvent(String sourceOfTruth, Status status, String transactionId,
-            UEBNotification notification, Map<Vertex, Introspector> deleteObjects, Map<String, URI> uriMap,
-            Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) {
-        for (Map.Entry<Vertex, Introspector> entry : deleteObjects.entrySet()) {
-            try {
-                if (null != entry.getValue()) {
-                    String vertexObjectId = entry.getValue().getObjectId();
-
-                    if (uriMap.containsKey(vertexObjectId) && deleteRelatedObjects.containsKey(vertexObjectId)) {
-                        notification.createNotificationEvent(transactionId, sourceOfTruth, status,
-                                uriMap.get(vertexObjectId), entry.getValue(), deleteRelatedObjects.get(vertexObjectId),
-                                basePath);
-                    }
-                }
-            } catch (UnsupportedEncodingException | AAIException e) {
-
-                LOGGER.warn("Error in sending notification");
-            }
-        }
-    }
 }
@@ -18,7 +18,7 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.aai.rest.ueb;
+package org.onap.aai.rest.notification;
 
 import org.onap.aai.exceptions.AAIException;
 import org.onap.aai.introspection.Introspector;
diff --git a/aai-core/src/main/java/org/onap/aai/rest/notification/NotificationService.java b/aai-core/src/main/java/org/onap/aai/rest/notification/NotificationService.java
new file mode 100644 (file)
index 0000000..d6a3f89
--- /dev/null
@@ -0,0 +1,196 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright Â© 2024 Deutsche Telekom. 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=========================================================
+ */
+
+package org.onap.aai.rest.notification;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.onap.aai.db.props.AAIProperties;
+import org.onap.aai.exceptions.AAIException;
+import org.onap.aai.introspection.Introspector;
+import org.onap.aai.introspection.LoaderFactory;
+import org.onap.aai.prevalidation.ValidationService;
+import org.onap.aai.rest.db.HttpEntry;
+import org.onap.aai.serialization.db.DBSerializer;
+import org.onap.aai.serialization.engines.query.QueryEngine;
+import org.onap.aai.setup.SchemaVersion;
+import org.onap.aai.util.AAIConfig;
+import org.onap.aai.util.delta.DeltaEvents;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Service
+public class NotificationService {
+
+  public static final Logger LOGGER = LoggerFactory.getLogger(NotificationService.class);
+
+  private final LoaderFactory loaderFactory;
+  private final boolean isDeltaEventsEnabled;
+  private final String basePath;
+
+  public NotificationService(
+    LoaderFactory loaderFactory,
+    @Value("${schema.uri.base.path}") String basePath,
+    @Value("${delta.events.enabled:false}") boolean isDeltaEventsEnabled) {
+    this.loaderFactory = loaderFactory;
+    this.basePath = basePath;
+    this.isDeltaEventsEnabled = isDeltaEventsEnabled;
+  }
+
+  /**
+   * Inject the validation service if the profile pre-valiation is enabled,
+   * Otherwise this variable will be set to null and thats why required=false
+   * so that it can continue even if pre validation isn't enabled
+   */
+  @Autowired(required = false)
+  private ValidationService validationService;
+
+  /**
+   * Generate notification events for the resulting db requests.
+   */
+  public void generateEvents(UEBNotification notification, int notificationDepth, String sourceOfTruth, DBSerializer serializer,
+      String transactionId,
+      QueryEngine queryEngine, Set<Vertex> mainVertexesToNotifyOn, SchemaVersion schemaVersion) throws AAIException {
+    if (notificationDepth == AAIProperties.MINIMUM_DEPTH) {
+      serializer.getUpdatedVertexes().entrySet().stream()
+        .filter(Map.Entry::getValue)
+        .map(Map.Entry::getKey)
+        .forEach(mainVertexesToNotifyOn::add);
+    }
+    Set<Vertex> edgeVertexes = serializer.touchStandardVertexPropertiesForEdges().stream()
+        .filter(v -> !mainVertexesToNotifyOn.contains(v))
+        .collect(Collectors.toSet());
+
+    try {
+      createNotificationEvents(mainVertexesToNotifyOn, notification, sourceOfTruth, serializer, transactionId, queryEngine,
+          notificationDepth, schemaVersion);
+      if ("true".equals(AAIConfig.get("aai.notification.both.sides.enabled", "true"))) {
+        createNotificationEvents(edgeVertexes, notification, sourceOfTruth, serializer, transactionId, queryEngine,
+            AAIProperties.MINIMUM_DEPTH, schemaVersion);
+      }
+    } catch (UnsupportedEncodingException e) {
+      LOGGER.warn("Encountered exception generating events", e);
+    }
+
+    // Since @Autowired required is set to false, we need to do a null check
+    // for the existence of the validationService since its only enabled if profile
+    // is enabled
+    if (validationService != null) {
+      validationService.validate(notification.getEvents());
+    }
+
+    notification.triggerEvents();
+    if (isDeltaEventsEnabled) {
+      try {
+        DeltaEvents deltaEvents = new DeltaEvents(transactionId, sourceOfTruth, schemaVersion.toString(),
+            serializer.getObjectDeltas());
+        deltaEvents.triggerEvents();
+      } catch (Exception e) {
+        LOGGER.error("Error sending Delta Events", e);
+      }
+    }
+  }
+
+  /**
+   * Generate notification events for provided set of vertexes at the specified
+   * depth
+   */
+  private void createNotificationEvents(Set<Vertex> vertexesToNotifyOn, UEBNotification notification, String sourceOfTruth, DBSerializer serializer,
+      String transactionId, QueryEngine queryEngine, int eventDepth, SchemaVersion schemaVersion)
+      throws AAIException, UnsupportedEncodingException {
+    for (Vertex vertex : vertexesToNotifyOn) {
+      if (canGenerateEvent(vertex)) {
+        boolean isVertexNew = vertex.value(AAIProperties.CREATED_TS).equals(vertex.value(AAIProperties.LAST_MOD_TS));
+        Status curObjStatus = isVertexNew ? Status.CREATED : Status.OK;
+
+        Introspector curObj = serializer.getLatestVersionView(vertex, eventDepth);
+        String aaiUri = vertex.<String>property(AAIProperties.AAI_URI).value();
+        String uri = String.format("%s/%s%s", basePath, schemaVersion, aaiUri);
+        HashMap<String, Introspector> curRelatedObjs = new HashMap<>();
+        if (!curObj.isTopLevel()) {
+          curRelatedObjs = serializer.getRelatedObjects(queryEngine, vertex, curObj, loaderFactory.getMoxyLoaderInstance().get(schemaVersion));
+        }
+        notification.createNotificationEvent(transactionId, sourceOfTruth, curObjStatus, URI.create(uri),
+            curObj, curRelatedObjs, basePath);
+      }
+    }
+  }
+
+  /**
+   * Verifies that vertex has needed properties to generate on
+   *
+   * @param vertex Vertex to be verified
+   * @return <code>true</code> if vertex has necessary properties and exists
+   */
+  private boolean canGenerateEvent(Vertex vertex) {
+    boolean canGenerate = true;
+    try {
+      if (!vertex.property(AAIProperties.AAI_URI).isPresent()) {
+        LOGGER.debug("Encountered an vertex {} with missing aai-uri", vertex.id());
+        canGenerate = false;
+      } else if (!vertex.property(AAIProperties.CREATED_TS).isPresent()
+          || !vertex.property(AAIProperties.LAST_MOD_TS).isPresent()) {
+        LOGGER.debug("Encountered an vertex {} with missing timestamp", vertex.id());
+        canGenerate = false;
+      }
+    } catch (IllegalStateException e) {
+      if (e.getMessage().contains(" was removed")) {
+        LOGGER.warn("Attempted to generate event for non existent vertex", e);
+      } else {
+        LOGGER.warn("Encountered exception generating events", e);
+      }
+      canGenerate = false;
+    }
+    return canGenerate;
+  }
+
+  public void buildNotificationEvent(String sourceOfTruth, Status status, String transactionId,
+      UEBNotification notification, Map<Vertex, Introspector> deleteObjects, Map<String, URI> uriMap,
+      Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) {
+    for (Map.Entry<Vertex, Introspector> entry : deleteObjects.entrySet()) {
+      try {
+        if (null != entry.getValue()) {
+          String vertexObjectId = entry.getValue().getObjectId();
+
+          if (uriMap.containsKey(vertexObjectId) && deleteRelatedObjects.containsKey(vertexObjectId)) {
+            notification.createNotificationEvent(transactionId, sourceOfTruth, status,
+                uriMap.get(vertexObjectId), entry.getValue(), deleteRelatedObjects.get(vertexObjectId),
+                basePath);
+          }
+        }
+      } catch (UnsupportedEncodingException | AAIException e) {
+
+        LOGGER.warn("Error in sending notification");
+      }
+    }
+  }
+
+}
@@ -18,7 +18,7 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.aai.rest.ueb;
+package org.onap.aai.rest.notification;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
index e1fc351..16f21ff 100644 (file)
@@ -33,6 +33,7 @@ import org.onap.aai.nodes.NodeIngestor;
 import org.onap.aai.prevalidation.ValidationConfiguration;
 import org.onap.aai.prevalidation.ValidationService;
 import org.onap.aai.rest.db.HttpEntry;
+import org.onap.aai.rest.notification.NotificationService;
 import org.onap.aai.serialization.db.EdgeSerializer;
 import org.onap.aai.serialization.queryformats.QueryFormatTestHelper;
 import org.onap.aai.setup.AAIConfigTranslator;
@@ -51,7 +52,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
 @ContextConfiguration(
         classes = {ConfigConfiguration.class, AAIConfigTranslator.class, EdgeIngestor.class, EdgeSerializer.class,
                 NodeIngestor.class, SpringContextAware.class, IntrospectionConfig.class, RestBeanConfig.class,
-                XmlFormatTransformerConfiguration.class, ValidationService.class, ValidationConfiguration.class})
+                XmlFormatTransformerConfiguration.class, ValidationService.class, ValidationConfiguration.class, LoaderFactory.class, NotificationService.class})
 @TestPropertySource(
         properties = {"schema.uri.base.path = /aai", "schema.xsd.maxoccurs = 5000", "schema.translator.list=config",
                 "schema.nodes.location=src/test/resources/onap/oxm",
index b878204..b38a5c6 100644 (file)
@@ -31,6 +31,7 @@ import org.onap.aai.introspection.LoaderFactory;
 import org.onap.aai.introspection.MoxyLoader;
 import org.onap.aai.nodes.NodeIngestor;
 import org.onap.aai.rest.db.HttpEntry;
+import org.onap.aai.rest.notification.NotificationService;
 import org.onap.aai.serialization.db.EdgeSerializer;
 import org.onap.aai.serialization.queryformats.QueryFormatTestHelper;
 import org.onap.aai.setup.SchemaVersion;
@@ -48,7 +49,7 @@ import org.springframework.test.context.junit4.rules.SpringMethodRule;
 @ContextConfiguration(
         classes = {ConfigConfiguration.class, TestUtilConfigTranslatorforDataLink.class, EdgeIngestor.class,
                 EdgeSerializer.class, NodeIngestor.class, SpringContextAware.class, IntrospectionConfig.class,
-                RestBeanConfig.class, XmlFormatTransformerConfiguration.class})
+                RestBeanConfig.class, XmlFormatTransformerConfiguration.class, LoaderFactory.class, NotificationService.class})
 @TestPropertySource(
         properties = {"schema.uri.base.path = /aai", "schema.xsd.maxoccurs = 5000", "schema.version.api.default = v4",
                 "schema.version.edge.label.start = v4", "schema.version.depth.start = v3",
index 07764ec..49524c4 100644 (file)
@@ -52,7 +52,7 @@ import org.onap.aai.parsers.query.QueryParser;
 import org.onap.aai.parsers.uri.URIToObject;
 import org.onap.aai.rest.db.DBRequest;
 import org.onap.aai.rest.db.HttpEntry;
-import org.onap.aai.rest.ueb.UEBNotification;
+import org.onap.aai.rest.notification.UEBNotification;
 import org.onap.aai.restcore.HttpMethod;
 import org.onap.aai.restcore.RESTAPI;
 import org.onap.aai.serialization.engines.QueryStyle;
index 138723a..368a946 100644 (file)
@@ -218,7 +218,7 @@ public class DataLinkTest extends DataLinkSetup {
 
     }
 
-    /** 
+    /**
      * This is more directly testing the modification mechanism (see verifyModificationOfVertex test)
      */
     @Test
@@ -229,7 +229,7 @@ public class DataLinkTest extends DataLinkSetup {
         URI uri = new URI("/network/vpn-bindings/vpn-binding/modifyKey/route-targets/route-target/modifyTargetKey2/modifyRoleKey2");
         MultivaluedMap<String, String> map = URITools.getQueryMap(uri);
         GraphTraversal<Vertex, Vertex> traversal = __.<Vertex>start();
-        
+
         QueryParser uriQuery = dbEngine.getQueryBuilder(this.queryStyle, loader, source, traversal).createQueryFromURI(uri, map);
         List<Vertex> results = uriQuery.getQueryBuilder().toList();
 
@@ -390,4 +390,3 @@ public class DataLinkTest extends DataLinkSetup {
 
     }
  }
\ No newline at end of file
index 04a2991..7651d40 100644 (file)
@@ -52,8 +52,6 @@ import org.onap.aai.TinkerpopUpgrade;
 import org.onap.aai.db.props.AAIProperties;
 import org.onap.aai.exceptions.AAIException;
 import org.onap.aai.introspection.ModelType;
-import org.onap.aai.query.builder.Pageable;
-import org.onap.aai.query.builder.QueryBuilder;
 import org.onap.aai.rest.RestTokens;
 import org.onap.aai.serialization.engines.JanusGraphDBEngine;
 import org.onap.aai.serialization.engines.QueryStyle;
index 6d10115..8f4a209 100644 (file)
@@ -52,6 +52,7 @@ import org.onap.aai.introspection.Loader;
 import org.onap.aai.introspection.LoaderFactory;
 import org.onap.aai.introspection.ModelType;
 import org.onap.aai.nodes.NodeIngestor;
+import org.onap.aai.rest.notification.NotificationService;
 import org.onap.aai.serialization.db.EdgeSerializer;
 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
 import org.onap.aai.serialization.queryformats.QueryFormatTestHelper;
@@ -67,7 +68,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 @ContextConfiguration(
         classes = {ConfigConfiguration.class, QueryTestsConfigTranslator.class, NodeIngestor.class, EdgeIngestor.class,
                 EdgeSerializer.class, SpringContextAware.class, IntrospectionConfig.class,
-                XmlFormatTransformerConfiguration.class})
+                XmlFormatTransformerConfiguration.class, LoaderFactory.class, NotificationService.class})
 @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
 @TestPropertySource(
         properties = {"schema.translator.list = config", "schema.nodes.location=src/test/resources/onap/oxm",
index e6664e1..218c3a2 100644 (file)
@@ -46,8 +46,8 @@ import org.onap.aai.PayloadUtil;
 import org.onap.aai.db.props.AAIProperties;
 import org.onap.aai.dbmap.AAIGraph;
 import org.onap.aai.introspection.ModelType;
-import org.onap.aai.rest.ueb.NotificationEvent;
-import org.onap.aai.rest.ueb.UEBNotification;
+import org.onap.aai.rest.notification.NotificationEvent;
+import org.onap.aai.rest.notification.UEBNotification;
 import org.onap.aai.serialization.engines.QueryStyle;
 import org.skyscreamer.jsonassert.JSONAssert;
 import org.slf4j.Logger;
index 399ef7e..4b52462 100644 (file)
@@ -57,8 +57,8 @@ import org.onap.aai.db.props.AAIProperties;
 import org.onap.aai.dbmap.AAIGraph;
 import org.onap.aai.exceptions.AAIException;
 import org.onap.aai.introspection.ModelType;
-import org.onap.aai.rest.ueb.NotificationEvent;
-import org.onap.aai.rest.ueb.UEBNotification;
+import org.onap.aai.rest.notification.NotificationEvent;
+import org.onap.aai.rest.notification.UEBNotification;
 import org.onap.aai.serialization.engines.QueryStyle;
 import org.skyscreamer.jsonassert.JSONAssert;
 import org.springframework.test.annotation.DirtiesContext;
index 3953998..ccc2c74 100644 (file)
@@ -95,8 +95,8 @@
  import org.onap.aai.rest.db.responses.Relationship;
  import org.onap.aai.rest.db.responses.RelationshipWrapper;
  import org.onap.aai.rest.db.responses.ServiceException;
- import org.onap.aai.rest.ueb.UEBNotification;
- import org.onap.aai.restcore.HttpMethod;
+import org.onap.aai.rest.notification.UEBNotification;
+import org.onap.aai.restcore.HttpMethod;
  import org.onap.aai.serialization.engines.QueryStyle;
  import org.onap.aai.serialization.engines.TransactionalGraphEngine;
  import org.onap.aai.util.AAIConfig;
index efa4325..5742258 100644 (file)
@@ -38,6 +38,7 @@ import org.onap.aai.exceptions.AAIException;
 import org.onap.aai.introspection.Introspector;
 import org.onap.aai.introspection.Loader;
 import org.onap.aai.introspection.ModelType;
+import org.onap.aai.rest.notification.UEBNotification;
 import org.onap.aai.serialization.db.EdgeSerializer;
 import org.onap.aai.serialization.engines.QueryStyle;
 import org.onap.aai.setup.SchemaVersion;
index ead8391..7dca441 100644 (file)
@@ -53,6 +53,7 @@ import org.onap.aai.introspection.LoaderFactory;
 import org.onap.aai.introspection.ModelType;
 import org.onap.aai.nodes.NodeIngestor;
 import org.onap.aai.parsers.query.QueryParser;
+import org.onap.aai.rest.notification.NotificationService;
 import org.onap.aai.serialization.engines.JanusGraphDBEngine;
 import org.onap.aai.serialization.engines.QueryStyle;
 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
@@ -70,7 +71,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 @ContextConfiguration(
         classes = {ConfigConfiguration.class, AAICoreFakeEdgesConfigTranslator.class, NodeIngestor.class,
                 EdgeIngestor.class, EdgeSerializer.class, SpringContextAware.class, IntrospectionConfig.class,
-                XmlFormatTransformerConfiguration.class})
+                XmlFormatTransformerConfiguration.class, LoaderFactory.class, NotificationService.class})
 @TestPropertySource(
         properties = {"schema.translator.list = config", "schema.nodes.location=src/test/resources/onap/oxm",
                 "schema.edges.location=src/test/resources/onap/dbedgerules"})
index 738953d..dff741e 100644 (file)
@@ -301,7 +301,7 @@ public class GraphTraversalQueryEngineTest extends AAISetup {
     /**
      * convenience helper method to make it easier to check the contents of the tree against
      * a list of expected results
-     * 
+     *
      * @param tree - the tree whose contents you want in collection form
      * @return set of the contents of the tree
      */