Changed EventTransformation schema to be loaded only once 40/100240/8
authorawudzins <adam.wudzinski@nokia.com>
Mon, 13 Jan 2020 10:43:08 +0000 (11:43 +0100)
committerawudzins <adam.wudzinski@nokia.com>
Fri, 17 Jan 2020 13:39:20 +0000 (14:39 +0100)
Issue-ID: DCAEGEN2-1774
Signed-off-by: adamwudzinski <adam.wudzinski@nokia.com>
Change-Id: I50f3da2c11201a40be948ab199aaca89bbbb38db

12 files changed:
pom.xml
src/main/java/org/onap/dcae/ApplicationSettings.java
src/main/java/org/onap/dcae/VesApplication.java
src/main/java/org/onap/dcae/common/EventTransformation.java [moved from src/main/java/org/onap/dcae/common/Event.java with 88% similarity]
src/main/java/org/onap/dcae/common/EventUpdater.java
src/main/java/org/onap/dcae/common/HeaderUtils.java
src/main/java/org/onap/dcae/restapi/EventValidator.java
src/test/java/org/onap/dcae/ApplicationSettingsTest.java
src/test/java/org/onap/dcae/restapi/EventValidatorTest.java
src/test/java/org/onap/dcae/restapi/VesRestControllerTest.java [new file with mode: 0644]
src/test/resources/api_version_config.json [new file with mode: 0644]
src/test/resources/eventTransform.json [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
index 5dfcaf4..4bdad84 100644 (file)
--- a/pom.xml
+++ b/pom.xml
       <artifactId>api-custom-header</artifactId>\r
       <version>1.1.4</version>\r
     </dependency>\r
+    <dependency>\r
+      <groupId>org.functionaljava</groupId>\r
+      <artifactId>functionaljava</artifactId>\r
+      <version>4.8.1</version>\r
+      <scope>compile</scope>\r
+    </dependency>\r
   </dependencies>\r
   <repositories>\r
     <repository>\r
index 1e9ae69..8458df8 100644 (file)
@@ -3,7 +3,7 @@
  * PROJECT
  * ================================================================================
  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
- * Copyright (C) 2018 - 2019 Nokia. All rights reserved.s
+ * Copyright (C) 2018 - 2020 Nokia. All rights reserved.s
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 package org.onap.dcae;
 
+import static java.lang.String.format;
+
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
 import com.networknt.schema.JsonSchema;
 import io.vavr.Function1;
 import io.vavr.collection.HashMap;
-import io.vavr.collection.List;
 import io.vavr.collection.Map;
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import javax.annotation.Nullable;
 import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.configuration.PropertiesConfiguration;
+import org.onap.dcae.common.EventTransformation;
 import org.onap.dcae.common.configuration.AuthMethodType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import javax.annotation.Nullable;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import static java.lang.String.format;
 
 /**
  * Abstraction over application configuration.
@@ -43,13 +50,16 @@ import static java.lang.String.format;
  */
 public class ApplicationSettings {
 
+    private static final String EVENT_TRANSFORM_FILE_PATH = "./etc/eventTransform.json";
+    private static final String COULD_NOT_FIND_FILE = "Couldn't find file " + EVENT_TRANSFORM_FILE_PATH;
+
     private static final Logger log = LoggerFactory.getLogger(ApplicationSettings.class);
     private static final String FALLBACK_VES_VERSION = "v5";
     private final String appInvocationDir;
     private final String configurationFileLocation;
     private final PropertiesConfiguration properties = new PropertiesConfiguration();
-    private final JSonSchemasSupplier jSonSchemasSupplier = new JSonSchemasSupplier();
     private final Map<String, JsonSchema> loadedJsonSchemas;
+    private final List<EventTransformation> eventTransformations;
 
     public ApplicationSettings(String[] args, Function1<String[], Map<String, String>> argsParser) {
         this(args, argsParser, System.getProperty("user.dir"));
@@ -64,7 +74,8 @@ public class ApplicationSettings {
         parsedArgs.filterKeys(k -> !"c".equals(k)).forEach(this::addOrUpdate);
         String collectorSchemaFile = properties.getString("collector.schema.file",
                 format("{\"%s\":\"etc/CommonEventFormat_28.4.1.json\"}", FALLBACK_VES_VERSION));
-        loadedJsonSchemas = jSonSchemasSupplier.loadJsonSchemas(collectorSchemaFile);
+        loadedJsonSchemas = new JSonSchemasSupplier().loadJsonSchemas(collectorSchemaFile);
+        eventTransformations = loadEventTransformations();
     }
 
     public void reloadProperties() {
@@ -76,22 +87,22 @@ public class ApplicationSettings {
             throw new ApplicationException(ex);
         }
     }
+
     public Map<String, String> validAuthorizationCredentials() {
         return prepareUsersMap(properties.getString("header.authlist", null));
     }
-
     public Path configurationFileLocation() {
         return Paths.get(configurationFileLocation);
     }
 
-    public boolean jsonSchemaValidationEnabled() {
+    public boolean eventSchemaValidationEnabled() {
         return properties.getInt("collector.schema.checkflag", -1) > 0;
     }
 
     public JsonSchema jsonSchema(String version) {
         return loadedJsonSchemas.get(version)
-                .orElse(loadedJsonSchemas.get(FALLBACK_VES_VERSION))
-                .getOrElseThrow(() -> new IllegalStateException("No fallback schema present in application."));
+            .orElse(loadedJsonSchemas.get(FALLBACK_VES_VERSION))
+            .getOrElseThrow(() -> new IllegalStateException("No fallback schema present in application."));
     }
 
     public boolean isVersionSupported(String version){
@@ -159,6 +170,10 @@ public class ApplicationSettings {
         }
     }
 
+    public List<EventTransformation> getEventTransformations() {
+        return eventTransformations;
+    }
+
     private void loadPropertiesFromFile() {
         try {
             properties.load(configurationFileLocation);
@@ -182,7 +197,7 @@ public class ApplicationSettings {
 
     private Map<String, String> prepareUsersMap(@Nullable String allowedUsers) {
         return allowedUsers == null ? HashMap.empty()
-            : List.of(allowedUsers.split("\\|"))
+            : io.vavr.collection.List.of(allowedUsers.split("\\|"))
                 .map(t->t.split(","))
                 .toMap(t-> t[0].trim(), t -> t[1].trim());
     }
@@ -205,6 +220,18 @@ public class ApplicationSettings {
         return filePath;
     }
 
+    private List<EventTransformation> loadEventTransformations() {
+        Type EVENT_TRANSFORM_LIST_TYPE = new TypeToken<List<EventTransformation>>() {}.getType();
+
+        try (FileReader fr = new FileReader(EVENT_TRANSFORM_FILE_PATH)) {
+            log.info("parse " + EVENT_TRANSFORM_FILE_PATH + " file");
+            return new Gson().fromJson(fr, EVENT_TRANSFORM_LIST_TYPE);
+        } catch (IOException e) {
+            log.error(COULD_NOT_FIND_FILE, e);
+            throw new ApplicationException(COULD_NOT_FIND_FILE, e);
+        }
+    }
+
     @VisibleForTesting
     String getStringDirectly(String key) {
         return properties.getString(key);
index e334082..bc5b1a8 100644 (file)
@@ -3,6 +3,7 @@
  * PROJECT
  * ================================================================================
  * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2020 Nokia. 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.
@@ -47,7 +48,7 @@ public class VesApplication {
     private static final Logger incomingRequestsLogger = LoggerFactory.getLogger("org.onap.dcae.common.input");
     private static final Logger oplog = LoggerFactory.getLogger("org.onap.dcae.common.output");
     private static final Logger errorLog = LoggerFactory.getLogger("org.onap.dcae.common.error");
-    private static ApplicationSettings properties;
+    private static ApplicationSettings applicationSettings;
     private static ConfigurableApplicationContext context;
     private static ConfigLoader configLoader;
     private static ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
@@ -57,7 +58,7 @@ public class VesApplication {
 
     public static void main(String[] args) {
       app = new SpringApplication(VesApplication.class);
-      properties = new ApplicationSettings(args, CLIUtils::processCmdLine);
+      applicationSettings = new ApplicationSettings(args, CLIUtils::processCmdLine);
       scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
       init();
       app.setAddCommandLineProperties(true);
@@ -68,7 +69,7 @@ public class VesApplication {
     public static void restartApplication() {
       Thread thread = new Thread(() -> {
         context.close();
-        properties.reloadProperties();
+        applicationSettings.reloadProperties();
         scheduleFeatures.cancel(true);
         init();
         context = SpringApplication.run(VesApplication.class);
@@ -89,32 +90,32 @@ public class VesApplication {
 
     private static void createSchedulePoolExecutor() {
       scheduleFeatures = scheduledThreadPoolExecutor.scheduleAtFixedRate(configLoader::updateConfig,
-          properties.configurationUpdateFrequency(),
-          properties.configurationUpdateFrequency(),
+          applicationSettings.configurationUpdateFrequency(),
+          applicationSettings.configurationUpdateFrequency(),
           TimeUnit.MINUTES);
     }
 
     private static void createConfigLoader() {
       configLoader = ConfigLoader.create(getEventPublisher()::reconfigure,
-          Paths.get(properties.dMaaPConfigurationFileLocation()),
-          properties.configurationFileLocation());
+          Paths.get(applicationSettings.dMaaPConfigurationFileLocation()),
+          applicationSettings.configurationFileLocation());
     }
 
 
     private static EventPublisher getEventPublisher() {
       return EventPublisher.createPublisher(oplog, DMaaPConfigurationParser
-          .parseToDomainMapping(Paths.get(properties.dMaaPConfigurationFileLocation())).get());
+          .parseToDomainMapping(Paths.get(applicationSettings.dMaaPConfigurationFileLocation())).get());
     }
 
     private static Map<String, PublisherConfig> getDmapConfig() {
       return DMaaPConfigurationParser
-          .parseToDomainMapping(Paths.get(properties.dMaaPConfigurationFileLocation())).get();
+          .parseToDomainMapping(Paths.get(applicationSettings.dMaaPConfigurationFileLocation())).get();
     }
 
     @Bean
     @Lazy
     public ApplicationSettings applicationSettings() {
-        return properties;
+        return applicationSettings;
     }
 
     @Bean
@@ -132,7 +133,7 @@ public class VesApplication {
     @Bean
     @Qualifier("eventSender")
     public EventSender eventSender() {
-        return new EventSender(eventPublisher,properties);
+        return new EventSender(eventPublisher, applicationSettings);
     }
 
 }
@@ -3,6 +3,7 @@
  * PROJECT
  * ================================================================================
  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2020 Nokia. 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.
@@ -23,11 +24,11 @@ import com.google.gson.JsonObject;
 
 import java.util.List;
 
-class Event {
+public class EventTransformation {
     final JsonObject filter;
     final List<Processor> processors;
 
-    Event(JsonObject filter, List<Processor> processors) {
+    EventTransformation(JsonObject filter, List<Processor> processors) {
         this.filter = filter;
         this.processors = processors;
     }
index 1caa4f1..1469d47 100644 (file)
@@ -3,7 +3,7 @@
  * PROJECT
  * ================================================================================
  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
- * Copyright (C) 2019 Nokia. All rights reserved.s
+ * Copyright (C) 2020 Nokia. All rights reserved.s
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 package org.onap.dcae.common;
 
-import com.google.common.reflect.TypeToken;
-import com.google.gson.Gson;
-import java.io.FileReader;
-import java.io.IOException;
-import java.lang.reflect.Type;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 import org.json.JSONArray;
 import org.json.JSONObject;
-import org.onap.dcae.ApplicationException;
 import org.onap.dcae.ApplicationSettings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,12 +37,9 @@ public class EventUpdater {
   private static final String EVENT = "event";
   private static final String VES_UNIQUE_ID = "VESuniqueId";
   private static final String VES_VERSION = "VESversion";
-  private static final String COULD_NOT_FIND_FILE = "Couldn't find file ./etc/eventTransform.json";
-  private static final Type EVENT_LIST_TYPE = new TypeToken<List<Event>>() {}.getType();
   private static final Logger log = LoggerFactory.getLogger(EventSender.class);
   private static final String EVENT_LITERAL = "event";
   private static final String COMMON_EVENT_HEADER = "commonEventHeader";
-  private static final String EVENT_TRANSFORM = "./etc/eventTransform.json";
   private ApplicationSettings settings;
   private final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, MM dd yyyy hh:mm:ss z");
 
@@ -87,18 +78,15 @@ public class EventUpdater {
 
   private JSONObject overrideEvent(JSONObject event) {
     JSONObject jsonObject = addCurrentTimeToEvent(event);
+
     if (settings.eventTransformingEnabled()) {
-      try (FileReader fr = new FileReader(EVENT_TRANSFORM)) {
-        log.info("parse " + EVENT_TRANSFORM + " file");
-        List<Event> events = new Gson().fromJson(fr, EVENT_LIST_TYPE);
-        parseEventsJson(events, new ConfigProcessorAdapter(new ConfigProcessors(jsonObject)));
-      } catch (IOException e) {
-        log.error(COULD_NOT_FIND_FILE, e);
-        throw new ApplicationException(COULD_NOT_FIND_FILE, e);
-      }
+      List<EventTransformation> eventTransformations = settings.getEventTransformations();
+      applyMatchingTransformations(eventTransformations, new ConfigProcessorAdapter(new ConfigProcessors(jsonObject)));
     }
+
     if (jsonObject.has(VES_VERSION))
        jsonObject.remove(VES_VERSION);
+
     log.debug("Modified event:" + jsonObject);
     return jsonObject;
   }
@@ -112,8 +100,8 @@ public class EventUpdater {
     return event;
   }
 
-  private void parseEventsJson(List<Event> eventsTransform, ConfigProcessorAdapter configProcessorAdapter) {
-    for (Event eventTransform : eventsTransform) {
+  private void applyMatchingTransformations(List<EventTransformation> eventsTransforms, ConfigProcessorAdapter configProcessorAdapter) {
+    for (EventTransformation eventTransform : eventsTransforms) {
       JSONObject filterObj = new JSONObject(eventTransform.filter.toString());
       if (configProcessorAdapter.isFilterMet(filterObj)) {
         callProcessorsMethod(configProcessorAdapter, eventTransform.processors);
index a570309..c046fb4 100644 (file)
@@ -33,7 +33,7 @@ import org.springframework.stereotype.Component;
  * @author nil
  */
 @Component
-public final class HeaderUtils {
+public class HeaderUtils {
 
   public String getApiVerFilePath(String fileName) {
     return Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(fileName))
index 009247c..3261c3b 100644 (file)
@@ -3,7 +3,7 @@
  * PROJECT
  * ================================================================================
  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
- * Copyright (C) 2019 Nokia. All rights reserved.s
+ * Copyright (C) 2020 Nokia. 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.
  */
 package org.onap.dcae.restapi;
 
+import java.util.Optional;
 import org.json.JSONObject;
 import org.onap.dcae.ApplicationSettings;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.http.ResponseEntity;
-import java.util.Optional;
 public class EventValidator {
 
   private final SchemaValidator schemaValidator = new SchemaValidator();
@@ -36,7 +34,7 @@ public class EventValidator {
   }
 
   public Optional<ResponseEntity<String>> validate(JSONObject jsonObject, String type, String version){
-    if (applicationSettings.jsonSchemaValidationEnabled()) {
+    if (applicationSettings.eventSchemaValidationEnabled()) {
       if (jsonObject.has(type)) {
         if (!schemaValidator.conformsToSchema(jsonObject, applicationSettings.jsonSchema(version))) {
           return errorResponse(ApiException.SCHEMA_VALIDATION_FAILED);
index c4ba586..3d8a1a1 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * org.onap.dcaegen2.collectors.ves
  * ================================================================================
- * Copyright (C) 2018 - 2019 Nokia. All rights reserved.
+ * Copyright (C) 2018 - 2020 Nokia. All rights reserved.
  * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -244,7 +244,7 @@ public class ApplicationSettingsTest {
     public void shouldTellIfSchemaValidationIsEnabled() throws IOException {
         // when
         boolean jsonSchemaValidationEnabled = fromTemporaryConfiguration("collector.schema.checkflag=1")
-            .jsonSchemaValidationEnabled();
+            .eventSchemaValidationEnabled();
 
         // then
         assertTrue(jsonSchemaValidationEnabled);
@@ -253,7 +253,7 @@ public class ApplicationSettingsTest {
     @Test
     public void shouldByDefaultSchemaValidationBeDisabled() throws IOException {
         // when
-        boolean jsonSchemaValidationEnabled = fromTemporaryConfiguration().jsonSchemaValidationEnabled();
+        boolean jsonSchemaValidationEnabled = fromTemporaryConfiguration().eventSchemaValidationEnabled();
 
         // then
         assertFalse(jsonSchemaValidationEnabled);
@@ -422,4 +422,4 @@ public class ApplicationSettingsTest {
     private String sanitizePath(String path) {
         return Paths.get(path).toString();
     }
-}
\ No newline at end of file
+}
index 4ac3c48..5359510 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * org.onap.dcaegen2.collectors.ves
  * ================================================================================
- * Copyright (C) 2019 Nokia. All rights reserved.
+ * Copyright (C) 2020 Nokia. 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.
 
 package org.onap.dcae.restapi;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
 import com.networknt.schema.JsonSchema;
 import com.networknt.schema.JsonSchemaFactory;
+import java.util.Optional;
 import org.json.JSONObject;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -32,13 +37,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
 import org.onap.dcae.ApplicationSettings;
 import org.springframework.http.ResponseEntity;
 
-import java.io.IOException;
-import java.util.Optional;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
 @ExtendWith(MockitoExtension.class)
 public class EventValidatorTest {
     private static final String DUMMY_SCHEMA_VERSION = "v5";
@@ -60,7 +58,7 @@ public class EventValidatorTest {
     @Test
     public void shouldReturnEmptyOptionalOnJsonSchemaValidationDisabled() {
         //given
-        when(settings.jsonSchemaValidationEnabled()).thenReturn(false);
+        when(settings.eventSchemaValidationEnabled()).thenReturn(false);
 
         //when
         Optional<ResponseEntity<String>> result = sut.validate(jsonObject, DUMMY_TYPE, DUMMY_SCHEMA_VERSION);
@@ -73,7 +71,7 @@ public class EventValidatorTest {
     @Test
     public void shouldReturnInvalidJsonErrorOnWrongType() {
         //given
-        when(settings.jsonSchemaValidationEnabled()).thenReturn(true);
+        when(settings.eventSchemaValidationEnabled()).thenReturn(true);
 
         //when
         Optional<ResponseEntity<String>> result = sut.validate(jsonObject, "wrongType", DUMMY_SCHEMA_VERSION);
@@ -83,11 +81,11 @@ public class EventValidatorTest {
     }
 
     @Test
-    public void shouldReturnSchemaValidationFailedErrorOnInvalidJsonObjectSchema() throws IOException {
+    public void shouldReturnSchemaValidationFailedErrorOnInvalidJsonObjectSchema() {
         //given
         String schemaRejectingEverything = "{\"not\":{}}";
         mockJsonSchema(schemaRejectingEverything);
-        when(settings.jsonSchemaValidationEnabled()).thenReturn(true);
+        when(settings.eventSchemaValidationEnabled()).thenReturn(true);
 
         //when
         Optional<ResponseEntity<String>> result = sut.validate(jsonObject, DUMMY_TYPE, DUMMY_SCHEMA_VERSION);
@@ -97,11 +95,11 @@ public class EventValidatorTest {
     }
 
     @Test
-    public void shouldReturnEmptyOptionalOnValidJsonObjectSchema() throws IOException {
+    public void shouldReturnEmptyOptionalOnValidJsonObjectSchema() {
         //given
         String schemaAcceptingEverything = "{}";
         mockJsonSchema(schemaAcceptingEverything);
-        when(settings.jsonSchemaValidationEnabled()).thenReturn(true);
+        when(settings.eventSchemaValidationEnabled()).thenReturn(true);
 
         //when
         Optional<ResponseEntity<String>> result = sut.validate(jsonObject, DUMMY_TYPE, DUMMY_SCHEMA_VERSION);
@@ -110,6 +108,7 @@ public class EventValidatorTest {
         assertEquals(Optional.empty(), result);
     }
 
+
     private void mockJsonSchema(String jsonSchemaContent) {
         JsonSchemaFactory factory = JsonSchemaFactory.getInstance();
 
@@ -121,4 +120,4 @@ public class EventValidatorTest {
         return Optional.of(ResponseEntity.status(schemaValidationFailed.httpStatusCode)
                 .body(schemaValidationFailed.toJSON().toString()));
     }
-}
\ No newline at end of file
+}
diff --git a/src/test/java/org/onap/dcae/restapi/VesRestControllerTest.java b/src/test/java/org/onap/dcae/restapi/VesRestControllerTest.java
new file mode 100644 (file)
index 0000000..444e28e
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.s
+ * ================================================================================
+ * 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.dcae.restapi;
+
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import org.jetbrains.annotations.NotNull;
+import org.json.JSONArray;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.onap.dcae.ApplicationSettings;
+import org.onap.dcae.common.EventSender;
+import org.onap.dcae.common.EventTransformation;
+import org.onap.dcae.common.HeaderUtils;
+import org.slf4j.Logger;
+import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class VesRestControllerTest {
+
+    private static final String EVENT_TRANSFORM_FILE_PATH = "/eventTransform.json";
+
+    @InjectMocks
+    VesRestController vesRestController;
+
+    @Mock
+    ApplicationSettings applicationSettings;
+
+    @Mock
+    Logger logger;
+
+    @Mock
+    EventSender eventSender;
+
+    @Mock
+    HeaderUtils headerUtils;
+
+    @Test
+    public void shouldReportThatApiVersionIsNotSupported() {
+        // given
+        when(applicationSettings.isVersionSupported("v20")).thenReturn(false);
+        MockHttpServletRequest request = givenMockHttpServletRequest();
+
+        // when
+        final ResponseEntity<String> event = vesRestController.event("", "v20", request);
+
+        // then
+        assertThat(event.getStatusCodeValue()).isEqualTo(400);
+        assertThat(event.getBody()).isEqualTo("API version v20 is not supported");
+        verify(eventSender, never()).send(any(JSONArray.class));
+    }
+
+    @Test
+    public void shouldTransformEventAccordingToEventTransformFile() throws IOException {
+        //given
+        configureEventTransformations();
+        configureHeadersForEventListener();
+
+        MockHttpServletRequest request = givenMockHttpServletRequest();
+
+        String validEvent = new String(
+                Files.readAllBytes(Paths.get(this.getClass().getResource("/ves7_valid.json").getPath()))
+        );
+
+        //when
+        final ResponseEntity<String> response = vesRestController.event(validEvent, "v7", request);
+
+        //then
+        assertThat(response.getStatusCodeValue()).isEqualTo(202);
+        assertThat(response.getBody()).isEqualTo("Accepted");
+        verifyThatTransformedEventWasSend(eventSender, validEvent);
+    }
+
+    private void configureEventTransformations() throws IOException {
+        final List<EventTransformation> eventTransformations = loadEventTransformations();
+        when(applicationSettings.isVersionSupported("v7")).thenReturn(true);
+        when(applicationSettings.eventTransformingEnabled()).thenReturn(true);
+        when(applicationSettings.getEventTransformations()).thenReturn(eventTransformations);
+    }
+
+    private void configureHeadersForEventListener() {
+        when(headerUtils.getRestApiIdentify(anyString())).thenReturn("eventListener");
+        when(headerUtils.getApiVerFilePath(anyString())).thenReturn(
+                this.getClass().getResource("/api_version_config.json").getPath()
+        );
+    }
+
+    private void verifyThatTransformedEventWasSend(EventSender eventSender, String eventBeforeTransformation) {
+        // event before transformation
+        assertThat(eventBeforeTransformation).contains("\"version\": \"4.0.1\"");
+        assertThat(eventBeforeTransformation).contains("\"faultFieldsVersion\": \"4.0\"");
+
+        ArgumentCaptor<JSONArray> argument = ArgumentCaptor.forClass(JSONArray.class);
+        verify(eventSender).send(argument.capture());
+
+        final String transformedEvent = argument.getValue().toString();
+
+        // event after transformation
+        assertThat(transformedEvent).contains("\"priority\":\"High\",\"version\":3,");
+        assertThat(transformedEvent).contains(",\"faultFieldsVersion\":3,\"specificProblem");
+    }
+
+    @NotNull
+    private MockHttpServletRequest givenMockHttpServletRequest() {
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setContentType("application/json");
+
+        RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
+        return request;
+    }
+
+    private List<EventTransformation> loadEventTransformations() throws IOException {
+        Type EVENT_TRANSFORM_LIST_TYPE = new TypeToken<List<EventTransformation>>() {
+        }.getType();
+
+        try (FileReader fr = new FileReader(this.getClass().getResource(EVENT_TRANSFORM_FILE_PATH).getPath())) {
+            return new Gson().fromJson(fr, EVENT_TRANSFORM_LIST_TYPE);
+        }
+    }
+}
diff --git a/src/test/resources/api_version_config.json b/src/test/resources/api_version_config.json
new file mode 100644 (file)
index 0000000..23f585f
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "apiVersion": 
+  {
+    "eventListener": ["4.7.2","5.4.1","7.0.1"],
+    "eventListener_eventBatch": ["4.7.2","5.4.1","7.0.1"]
+  }
+}
diff --git a/src/test/resources/eventTransform.json b/src/test/resources/eventTransform.json
new file mode 100644 (file)
index 0000000..ab37a6e
--- /dev/null
@@ -0,0 +1,396 @@
+[
+  {
+    "filter": {
+      "event.commonEventHeader.domain": "heartbeat",
+      "VESversion": "v7"
+    },
+    "processors": [
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.commonEventHeader.version",
+          "value": "3.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.heartbeatFields.heartbeatFieldsVersion",
+          "value": "2.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.heartbeatFields.additionalFields",
+          "mapType": "HashmapToNameValueArray"
+        }
+      }
+    ]
+  },
+  {
+    "filter": {
+      "event.commonEventHeader.domain": "fault",
+      "VESversion": "v7"
+    },
+    "processors": [
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.commonEventHeader.version",
+          "value": "3.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.faultFields.faultFieldsVersion",
+          "value": "3.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.faultFields.alarmAdditionalInformation",
+          "mapType": "HashmapToNameValueArray"
+        }
+      }
+    ]
+  },
+  {
+    "filter": {
+      "event.commonEventHeader.domain": "thresholdCrossingAlert",
+      "VESversion": "v7"
+    },
+    "processors": [
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.commonEventHeader.version",
+          "value": "3.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.thresholdCrossingFields.thresholdCrossingFieldsVersion",
+          "value": "3.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.thresholdCrossingFields.additionalFields",
+          "mapType": "HashmapToNameValueArray"
+        }
+      }
+    ]
+  },
+  {
+    "filter": {
+      "event.commonEventHeader.domain": "measurement",
+      "VESversion": "v7"
+    },
+    "processors": [
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.commonEventHeader.version",
+          "value": "3.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "removeAttribute",
+        "args": {
+          "field": "event.measurementFields.measurementFieldsVersion"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.measurementFields.measurementsForVfScalingVersion",
+          "value": "3.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.measurementFields.vNicPerformanceArray[]",
+          "oldField": "event.measurementFields.nicPerformanceArray[]",
+          "attrMap": {
+            "nicIdentifier": "vNicIdentifier"
+          }
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.measurementFields.additionalFields",
+          "oldField": "event.measurementFields.additionalFields",
+          "mapType": "hashmapToNameValueArray"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.measurementsForVfScalingFields",
+          "oldField": "event.measurementFields",
+          "mapType": "renameObject"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.commonEventHeader.domain",
+          "value": "measurementsForVfScaling"
+        }
+      }
+    ]
+  },
+  {
+    "filter": {
+      "event.commonEventHeader.domain": "heartbeat",
+      "VESversion": "v4"
+    },
+    "processors": [
+      {
+        "functionName": "concatenateValue",
+        "args": {
+          "field": "event.commonEventHeader.eventName",
+          "concatenate": [
+            "$event.commonEventHeader.domain",
+            "$event.commonEventHeader.eventType",
+            "$event.faultFields.alarmCondition"
+          ],
+          "delimiter": "_"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.heartbeatFields.heartbeatFieldsVersion",
+          "value": "1.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.heartbeatFields.heartbeatInterval",
+          "value": "0",
+          "fieldType": "integer"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.commonEventHeader.nfNamingCode",
+          "oldField": "event.commonEventHeader.functionalRole"
+        }
+      }
+    ]
+  },
+  {
+    "filter": {
+      "event.commonEventHeader.domain": "fault",
+      "VESversion": "v4"
+    },
+    "processors": [
+      {
+        "functionName": "concatenateValue",
+        "args": {
+          "field": "event.commonEventHeader.eventName",
+          "concatenate": [
+            "$event.commonEventHeader.domain",
+            "$event.commonEventHeader.eventType",
+            "$event.faultFields.alarmCondition"
+          ],
+          "delimiter": "_"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.faultFields.faultFieldsVersion",
+          "value": "2.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.commonEventHeader.version",
+          "value": "3.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.commonEventHeader.nfNamingCode",
+          "oldField": "event.commonEventHeader.functionalRole"
+        }
+      }
+    ]
+  },
+  {
+    "filter": {
+      "event.commonEventHeader.domain": "thresholdCrossingAlert",
+      "VESversion": "v4"
+    },
+    "processors": [
+      {
+        "functionName": "concatenateValue",
+        "args": {
+          "field": "event.commonEventHeader.eventName",
+          "concatenate": [
+            "$event.commonEventHeader.domain",
+            "$event.commonEventHeader.elementType",
+            "$event.faultFields.alertDescription"
+          ],
+          "delimiter": "_"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.commonEventHeader.nfNamingCode",
+          "oldField": "event.commonEventHeader.functionalRole"
+        }
+      }
+    ]
+  },
+  {
+    "filter": {
+      "event.commonEventHeader.domain": "measurementsForVfScaling",
+      "VESversion": "v4",
+      "not": {
+        "event.commonEventHeader.reportingEntityName": "matches:.*ircc|irpr.*"
+      }
+    },
+    "processors": [
+      {
+        "functionName": "concatenateValue",
+        "args": {
+          "field": "event.commonEventHeader.eventName",
+          "concatenate": [
+            "$event.commonEventHeader.domain",
+            "$event.commonEventHeader.eventType",
+            "$event.faultFields.alarmCondition"
+          ],
+          "delimiter": "_"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.measurementsForVfScalingFields.measurementsForVfScalingVersion",
+          "value": "2.0",
+          "fieldType": "number"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.measurementsForVfScalingFields.additionalMeasurements[].arrayOfFields[]",
+          "oldField": "event.measurementsForVfScalingFields.additionalMeasurements[].measurements[]"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "oldField": "event.measurementsForVfScalingFields.aggregateCpuUsage",
+          "field": "event.measurementsForVfScalingFields.cpuUsageArray[0].percentUsage"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.measurementsForVfScalingFields.cpuUsageArray[0].cpuIdentifier",
+          "value": "$event.commonEventHeader.sourceName"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.measurementsForVfScalingFields.memoryUsageArray[0].memoryConfigured",
+          "oldField": "event.measurementsForVfScalingFields.memoryConfigured",
+          "operation": "convertMBtoKB"
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.measurementsForVfScalingFields.memoryUsageArray[0].memoryUsed",
+          "oldField": "event.measurementsForVfScalingFields.memoryUsed",
+          "operation": "convertMBtoKB"
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.measurementsForVfScalingFields.memoryUsageArray[0].vmIdentifier",
+          "value": "$event.commonEventHeader.sourceName"
+        }
+      },
+      {
+        "functionName": "subtractValue",
+        "args": {
+          "field": "event.measurementsForVfScalingFields.memoryUsageArray[0].memoryFree",
+          "subtract": [
+            "$event.measurementsForVfScalingFields.memoryUsageArray[0].memoryConfigured",
+            "$event.measurementsForVfScalingFields.memoryUsageArray[0].memoryUsed"
+          ]
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.measurementsForVfScalingFields.vNicPerformanceArray[]",
+          "oldField": "event.measurementsForVfScalingFields.vNicUsageArray[]",
+          "attrMap": {
+            "broadcastPacketsIn": "receivedBroadcastPacketsAccumulated",
+            "multicastPacketsIn": "receivedMulticastPacketsAccumulated",
+            "bytesIn": "receivedOctetsAccumulated",
+            "packetsIn": "receivedTotalPacketsAccumulated",
+            "unicastPacketsIn": "receivedUnicastPacketsAccumulated",
+            "broadcastPacketsOut": "transmittedBroadcastPacketsAccumulated",
+            "multicastPacketsOut": "transmittedMulticastPacketsAccumulated",
+            "bytesOut": "transmittedOctetsAccumulated",
+            "packetsOut": "transmittedTotalPacketsAccumulated",
+            "unicastPacketsOut": "transmittedUnicastPacketsAccumulated"
+          }
+        }
+      },
+      {
+        "functionName": "map",
+        "args": {
+          "field": "event.measurementsForVfScalingFields.vNicPerformanceArray[]",
+          "oldField": "event.measurementsForVfScalingFields.errors",
+          "attrMap": {
+            "receiveDiscards": "receivedDiscardedPacketsAccumulated",
+            "receiveErrors": "receivedErrorPacketsAccumulated",
+            "transmitDiscards": "transmittedDiscardedPacketsAccumulated",
+            "transmitErrors": "transmittedErrorPacketsAccumulated"
+          }
+        }
+      },
+      {
+        "functionName": "addAttribute",
+        "args": {
+          "field": "event.measurementsForVfScalingFields.vNicPerformanceArray[0].valuesAreSuspect",
+          "value": "false"
+        }
+      }
+    ]
+  }
+]
+