Break out configuration file read 16/114716/2
authorelinuxhenrik <henrik.b.andersson@est.tech>
Fri, 6 Nov 2020 14:15:42 +0000 (15:15 +0100)
committerelinuxhenrik <henrik.b.andersson@est.tech>
Mon, 9 Nov 2020 15:35:12 +0000 (16:35 +0100)
Break out the reading of the configuration file to a separate class to
prepare for the addition of a REST API to replace the configuration
file.

Change-Id: If61669563d8bc9b0b235a49a30fae3b3efbc5148
Issue-ID: CCSDK-2966
Signed-off-by: elinuxhenrik <henrik.b.andersson@est.tech>
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ConfigurationFile.java [new file with mode: 0644]
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/tasks/RefreshConfigTask.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ConfigurationFileTest.java [new file with mode: 0644]
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/tasks/RefreshConfigTaskTest.java

diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ConfigurationFile.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ConfigurationFile.java
new file mode 100644 (file)
index 0000000..345722c
--- /dev/null
@@ -0,0 +1,93 @@
+/*-
+ * ========================LICENSE_START=================================
+ * Copyright (C) 2020 Nordix Foundation. 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.ccsdk.oran.a1policymanagementservice.configuration;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Optional;
+import javax.validation.constraints.NotNull;
+import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ConfigurationFile {
+    private static final Logger logger = LoggerFactory.getLogger(ConfigurationFile.class);
+
+    final ApplicationConfig appConfig;
+    final Gson gson = new Gson();
+
+    @Autowired
+    public ConfigurationFile(ApplicationConfig appConfig) {
+        this.appConfig = appConfig;
+    }
+
+    public synchronized Optional<JsonObject> readFile() {
+        String filepath = appConfig.getLocalConfigurationFilePath();
+        if (!fileExists(filepath)) {
+            return Optional.empty();
+        }
+
+        try (InputStream inputStream = createInputStream(filepath)) {
+            JsonObject rootObject = getJsonElement(inputStream).getAsJsonObject();
+            logger.debug("Local configuration file loaded: {}", filepath);
+            return Optional.of(rootObject);
+        } catch (Exception e) {
+            logger.error("Local configuration file not loaded: {}, {}", filepath, e.getMessage());
+            return Optional.empty();
+        }
+    }
+
+    public synchronized void writeFile(JsonObject content) throws ServiceException {
+        String filepath = appConfig.getLocalConfigurationFilePath();
+        try (FileWriter fileWriter = getFileWriter(filepath)) {
+            gson.toJson(content, fileWriter);
+        } catch (IOException e) {
+            logger.error("Local configuration file not written: {}, {}", filepath, e.getMessage());
+            throw new ServiceException("Local configuration file not written");
+        }
+    }
+
+    FileWriter getFileWriter(String filepath) throws IOException {
+        return new FileWriter(filepath);
+    }
+
+    private boolean fileExists(String filepath) {
+        return (new File(filepath).exists());
+    }
+
+    private JsonElement getJsonElement(InputStream inputStream) {
+        return JsonParser.parseReader(new InputStreamReader(inputStream));
+    }
+
+    private InputStream createInputStream(@NotNull String filepath) throws IOException {
+        return new BufferedInputStream(new FileInputStream(filepath));
+    }
+}
index e4d7d8d..b834124 100644 (file)
 
 package org.onap.ccsdk.oran.a1policymanagementservice.tasks;
 
-import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
 
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.time.Duration;
+import java.util.Optional;
 import java.util.Properties;
 
-import javax.validation.constraints.NotNull;
-
 import lombok.AccessLevel;
 import lombok.Getter;
 
@@ -43,6 +34,7 @@ import org.onap.ccsdk.oran.a1policymanagementservice.clients.AsyncRestClientFact
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig.RicConfigUpdate;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfigParser;
+import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ConfigurationFile;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig;
 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies;
 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes;
@@ -83,6 +75,7 @@ public class RefreshConfigTask {
      */
     static final Duration CONFIG_REFRESH_INTERVAL = Duration.ofMinutes(1);
 
+    final ConfigurationFile configurationFile;
     final ApplicationConfig appConfig;
     @Getter(AccessLevel.PROTECTED)
     private Disposable refreshTask = null;
@@ -96,8 +89,9 @@ public class RefreshConfigTask {
     private final AsyncRestClientFactory restClientFactory;
 
     @Autowired
-    public RefreshConfigTask(ApplicationConfig appConfig, Rics rics, Policies policies, Services services,
-            PolicyTypes policyTypes, A1ClientFactory a1ClientFactory) {
+    public RefreshConfigTask(ConfigurationFile configurationFile, ApplicationConfig appConfig, Rics rics,
+            Policies policies, Services services, PolicyTypes policyTypes, A1ClientFactory a1ClientFactory) {
+        this.configurationFile = configurationFile;
         this.appConfig = appConfig;
         this.rics = rics;
         this.policies = policies;
@@ -195,10 +189,6 @@ public class RefreshConfigTask {
         return this.appConfig.setConfiguration(config);
     }
 
-    boolean fileExists(String filepath) {
-        return (filepath != null && (new File(filepath).exists()));
-    }
-
     private void removePoliciciesInRic(@Nullable Ric ric) {
         if (ric != null) {
             RicSynchronizationTask synch =
@@ -245,28 +235,10 @@ public class RefreshConfigTask {
      * Reads the configuration from file.
      */
     Flux<JsonObject> loadConfigurationFromFile() {
-        String filepath = appConfig.getLocalConfigurationFilePath();
-        if (!fileExists(filepath)) {
-            return Flux.empty();
-        }
-
-        try (InputStream inputStream = createInputStream(filepath)) {
-            JsonObject rootObject = getJsonElement(inputStream).getAsJsonObject();
-            ApplicationConfigParser appParser = new ApplicationConfigParser();
-            appParser.parse(rootObject);
-            logger.debug("Local configuration file loaded: {}", filepath);
-            return Flux.just(rootObject);
-        } catch (Exception e) {
-            logger.error("Local configuration file not loaded: {}, {}", filepath, e.getMessage());
-            return Flux.empty();
+        Optional<JsonObject> readJson = configurationFile.readFile();
+        if (readJson.isPresent()) {
+            return Flux.just(readJson.get());
         }
-    }
-
-    JsonElement getJsonElement(InputStream inputStream) {
-        return JsonParser.parseReader(new InputStreamReader(inputStream));
-    }
-
-    InputStream createInputStream(@NotNull String filepath) throws IOException {
-        return new BufferedInputStream(new FileInputStream(filepath));
+        return Flux.empty();
     }
 }
diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ConfigurationFileTest.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ConfigurationFileTest.java
new file mode 100644 (file)
index 0000000..ad4cbb8
--- /dev/null
@@ -0,0 +1,120 @@
+/*-
+ * ========================LICENSE_START=================================
+ * Copyright (C) 2020 Nordix Foundation. 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.ccsdk.oran.a1policymanagementservice.configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException;
+import org.onap.ccsdk.oran.a1policymanagementservice.utils.LoggingUtils;
+
+@ExtendWith(MockitoExtension.class)
+class ConfigurationFileTest {
+    @Mock
+    ApplicationConfig applicationConfigMock;
+
+    @TempDir
+    public File temporaryFolder;
+
+    @Test
+    void writeFileWithError_shouldThrowExceptionAndLogError() throws Exception {
+        File tempJsonFile = new File(temporaryFolder, "config.json");
+        String filePath = tempJsonFile.getAbsolutePath();
+
+        ConfigurationFile configFileUnderTestSpy = spy(new ConfigurationFile(applicationConfigMock));
+
+        when(applicationConfigMock.getLocalConfigurationFilePath()).thenReturn(filePath);
+        doThrow(new IOException("Error")).when(configFileUnderTestSpy).getFileWriter(any(String.class));
+
+        ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(ConfigurationFile.class, Level.ERROR);
+        Exception actualException =
+                assertThrows(ServiceException.class, () -> configFileUnderTestSpy.writeFile(new JsonObject()));
+
+        assertThat(actualException.getMessage()).startsWith("Local configuration file not written");
+
+        assertThat(logAppender.list.get(0).getFormattedMessage()).startsWith("Local configuration file not written");
+    }
+
+    @Test
+    void writeAndReadFile_shouldBeOk() throws Exception {
+        File tempJsonFile = new File(temporaryFolder, "config.json");
+        String filePath = tempJsonFile.getAbsolutePath();
+
+        ConfigurationFile configFileUnderTest = new ConfigurationFile(applicationConfigMock);
+
+        JsonObject content = JsonParser.parseString("{\"test\":\"test\"}").getAsJsonObject();
+
+        when(applicationConfigMock.getLocalConfigurationFilePath()).thenReturn(filePath);
+
+        configFileUnderTest.writeFile(content);
+
+        Optional<JsonObject> readContent = configFileUnderTest.readFile();
+
+        assertThat(readContent).isNotEmpty().hasValue(content);
+    }
+
+    @Test
+    void readWhenFileMissing_shouldReturnEmpty() {
+        ConfigurationFile configFileUnderTest = new ConfigurationFile(applicationConfigMock);
+
+        String filePath = "configFile.json";
+        when(applicationConfigMock.getLocalConfigurationFilePath()).thenReturn(filePath);
+
+        Optional<JsonObject> readContent = configFileUnderTest.readFile();
+
+        assertThat(readContent).isEmpty();
+    }
+
+    @Test
+    void readWhenFileWithIoError_shouldReturnEmptyAndLogError() throws Exception {
+        File tempJsonFile = new File(temporaryFolder, "config.json");
+        String filePath = tempJsonFile.getAbsolutePath();
+        Files.write(tempJsonFile.toPath(), "".getBytes());
+
+        ConfigurationFile configFileUnderTest = new ConfigurationFile(applicationConfigMock);
+
+        when(applicationConfigMock.getLocalConfigurationFilePath()).thenReturn(filePath);
+
+        ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(ConfigurationFile.class, Level.ERROR);
+        Optional<JsonObject> readContent = configFileUnderTest.readFile();
+
+        assertThat(readContent).isEmpty();
+
+        assertThat(logAppender.list.get(0).getFormattedMessage())
+                .isEqualTo("Local configuration file not loaded: " + filePath + ", Not a JSON Object: null");
+    }
+}
index 04951ee..07a6201 100644 (file)
@@ -34,28 +34,20 @@ import static org.mockito.Mockito.when;
 
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.read.ListAppender;
-
 import com.google.common.base.Charsets;
 import com.google.common.io.Resources;
-import com.google.gson.JsonIOException;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
-import com.google.gson.JsonSyntaxException;
-
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.net.URL;
-import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Optional;
 import java.util.Properties;
 import java.util.Vector;
-
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
@@ -66,6 +58,7 @@ import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationCo
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig.RicConfigUpdate.Type;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfigParser;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfigParser.ConfigParserResult;
+import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ConfigurationFile;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ImmutableConfigParserResult;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ImmutableRicConfig;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig;
@@ -81,7 +74,6 @@ import org.onap.ccsdk.oran.a1policymanagementservice.utils.LoggingUtils;
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsClient;
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.EnvProperties;
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.ImmutableEnvProperties;
-
 import reactor.core.publisher.Mono;
 import reactor.test.StepVerifier;
 
@@ -99,6 +91,9 @@ class RefreshConfigTaskTest {
     @Mock
     CbsClient cbsClient;
 
+    @Mock
+    ConfigurationFile configurationFileMock;
+
     private static final String RIC_1_NAME = "ric1";
     private static final RicConfig CORRECT_RIC_CONIFG = ImmutableRicConfig.builder() //
             .ricId(RIC_1_NAME) //
@@ -122,13 +117,11 @@ class RefreshConfigTaskTest {
 
     private RefreshConfigTask createTestObject(boolean configFileExists, Rics rics, Policies policies,
             boolean stubConfigFileExists) {
-        doReturn("fileName").when(appConfig).getLocalConfigurationFilePath();
-        doReturn(null).when(appConfig).getWebClientConfig();
 
-        RefreshConfigTask obj = spy(new RefreshConfigTask(appConfig, rics, policies, new Services(), new PolicyTypes(),
-                new A1ClientFactory(appConfig)));
+        RefreshConfigTask obj = spy(new RefreshConfigTask(configurationFileMock, appConfig, rics, policies,
+                new Services(), new PolicyTypes(), new A1ClientFactory(appConfig)));
         if (stubConfigFileExists) {
-            doReturn(configFileExists).when(obj).fileExists(any());
+            when(configurationFileMock.readFile()).thenReturn(Optional.empty());
         }
         return obj;
     }
@@ -138,7 +131,7 @@ class RefreshConfigTaskTest {
         refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_EXISTS);
         refreshTaskUnderTest.systemEnvironment = new Properties();
         // When
-        doReturn(getCorrectJson()).when(refreshTaskUnderTest).createInputStream(any());
+        when(configurationFileMock.readFile()).thenReturn(getCorrectJson());
 
         StepVerifier //
                 .create(refreshTaskUnderTest.createRefreshTask()) //
@@ -160,13 +153,12 @@ class RefreshConfigTaskTest {
     }
 
     @Test
-    void whenFileExistsButJsonIsIncorrect_thenNoRicsArePutInRepositoryAndErrorIsLogged() throws Exception {
+    void whenFileExistsButJsonIsIncorrect_thenNoRicsArePutInRepository() throws Exception {
         refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_EXISTS);
         refreshTaskUnderTest.systemEnvironment = new Properties();
 
         // When
-        final String JUNK_JSON = "{\"junk }";
-        doReturn(getJsonSteam(JUNK_JSON)).when(refreshTaskUnderTest).createInputStream(any());
+        when(configurationFileMock.readFile()).thenReturn(Optional.empty());
 
         final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class, ERROR);
 
@@ -180,10 +172,6 @@ class RefreshConfigTaskTest {
         // Then
         verify(refreshTaskUnderTest).loadConfigurationFromFile();
         assertThat(appConfig.getRicConfigs()).isEmpty();
-
-        await().until(() -> logAppender.list.size() > 0);
-        assertThat(logAppender.list.get(0).getFormattedMessage())
-                .startsWith("Local configuration file not loaded: fileName, ");
     }
 
     @Test
@@ -232,7 +220,7 @@ class RefreshConfigTaskTest {
         doReturn(Mono.just(props)).when(refreshTaskUnderTest).getEnvironment(any());
         doReturn(Mono.just(cbsClient)).when(refreshTaskUnderTest).createCbsClient(props);
 
-        JsonObject configAsJson = getJsonRootObject(getCorrectJson());
+        JsonObject configAsJson = getCorrectJson().get();
         String newBaseUrl = "newBaseUrl";
         modifyTheRicConfiguration(configAsJson, newBaseUrl);
         when(cbsClient.get(any())).thenReturn(Mono.just(configAsJson));
@@ -332,19 +320,9 @@ class RefreshConfigTaskTest {
                         .addProperty("baseUrl", newBaseUrl);
     }
 
-    private JsonObject getJsonRootObject(InputStream inStream)
-            throws JsonIOException, JsonSyntaxException, IOException {
-        JsonObject rootObject = JsonParser.parseReader(new InputStreamReader(inStream)).getAsJsonObject();
-        return rootObject;
-    }
-
-    private static InputStream getCorrectJson() throws IOException {
+    private static Optional<JsonObject> getCorrectJson() throws IOException {
         URL url = ApplicationConfigParser.class.getClassLoader().getResource("test_application_configuration.json");
         String string = Resources.toString(url, Charsets.UTF_8);
-        return new ByteArrayInputStream((string.getBytes(StandardCharsets.UTF_8)));
-    }
-
-    private static InputStream getJsonSteam(String json) {
-        return new ByteArrayInputStream((json.getBytes(StandardCharsets.UTF_8)));
+        return Optional.of(JsonParser.parseString(string).getAsJsonObject());
     }
 }