echo liveness probe available under /actuator/health 51/139051/2
authorShantaram Sawant <s.shantaram.sawant@accenture.com>
Tue, 1 Oct 2024 11:37:03 +0000 (17:07 +0530)
committerShantaram Sawant <s.shantaram.sawant@accenture.com>
Thu, 3 Oct 2024 11:16:13 +0000 (16:46 +0530)
- Include database connectivity check in the /echo API of AAI modules.

Issue-ID: AAI-3999
Change-Id: Ia6eb3ded705e6b5b3f1db0cd9605376a2f442e68
Signed-off-by: Shantaram Sawant <s.shantaram.sawant@accenture.com>
pom.xml
src/main/java/org/onap/aai/rest/util/AaiGraphChecker.java [new file with mode: 0644]
src/main/java/org/onap/aai/rest/util/EchoHealthIndicator.java [new file with mode: 0644]
src/main/java/org/onap/aai/rest/util/EchoResponse.java
src/main/resources/application.properties
src/test/java/org/onap/aai/config/WebClientConfiguration.java [new file with mode: 0644]
src/test/java/org/onap/aai/rest/util/EchoHealthIndicatorTest.java [new file with mode: 0644]
src/test/java/org/onap/aai/rest/util/EchoResponseTest.java [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
index 7f2b5f3..ac3ae76 100755 (executable)
--- a/pom.xml
+++ b/pom.xml
     </dependencyManagement>
     <dependencies>
         <!-- Start of graphadmin metric collection dependencies -->
+               <dependency>
+                       <groupId>org.projectlombok</groupId>
+                       <artifactId>lombok</artifactId>
+                       <version>1.18.30</version>
+                       <scope>provided</scope>
+               </dependency>
         <dependency>
             <groupId>io.micrometer</groupId>
             <artifactId>micrometer-core</artifactId>
             <version>${netty.handler.version}</version>
         </dependency>
         <!-- End of Netty Dependencies -->
+               <!-- Only used for the WebTestClient -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+            <scope>test</scope>
+        </dependency>
         <!-- Start of Antlr dependencies for DSL -->
         <dependency>
             <groupId>org.antlr</groupId>
diff --git a/src/main/java/org/onap/aai/rest/util/AaiGraphChecker.java b/src/main/java/org/onap/aai/rest/util/AaiGraphChecker.java
new file mode 100644 (file)
index 0000000..dab5cc3
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Bell Canada
+ * Modification Copyright (C) 2022 Deutsche Telekom SA
+ * ================================================================================
+ * 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.util;
+
+import java.util.Iterator;
+
+import org.janusgraph.core.JanusGraphException;
+import org.janusgraph.core.JanusGraphTransaction;
+import org.onap.aai.dbmap.AAIGraph;
+import org.onap.aai.logging.ErrorLogHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+/**
+ * Singleton class responsible to check that AAI service is able to connect to its back-end
+ * database.
+ */
+@Component
+public class AaiGraphChecker {
+    private static final Logger LOGGER = LoggerFactory.getLogger(AaiGraphChecker.class);
+
+    private AaiGraphChecker() {
+    }
+
+    /**
+     * Checks whether a connection to the graph database can be made.
+     * 
+     * @return
+     *         <li>true, if database is available</li>
+     *         <li>false, if database is NOT available</li>
+     */
+    public Boolean isAaiGraphDbAvailable() {
+        Boolean dbAvailable;
+        JanusGraphTransaction transaction = null;
+        try {
+            transaction = AAIGraph.getInstance().getGraph().newTransaction();
+            final Iterator<?> vertexIterator = transaction.query().limit(1).vertices().iterator();
+            vertexIterator.hasNext();
+            dbAvailable = Boolean.TRUE;
+        } catch (JanusGraphException e) {
+            String message = "Database is not available (after JanusGraph exception)";
+            ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+            LOGGER.error(message, e);
+            dbAvailable = Boolean.FALSE;
+        } catch (Error e) {
+            String message = "Database is not available (after error)";
+            ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+            LOGGER.error(message, e);
+            dbAvailable = Boolean.FALSE;
+        } catch (Exception e) {
+            String message = "Database availability can not be determined";
+            ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+            LOGGER.error(message, e);
+            dbAvailable = null;
+        } finally {
+            if (transaction != null && !transaction.isClosed()) {
+                // check if transaction is open then closed instead of flag
+                try {
+                    transaction.rollback();
+                } catch (Exception e) {
+                    String message = "Exception occurred while closing transaction";
+                    LOGGER.error(message, e);
+                    ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+                }
+            }
+        }
+        return dbAvailable;
+    }
+}
diff --git a/src/main/java/org/onap/aai/rest/util/EchoHealthIndicator.java b/src/main/java/org/onap/aai/rest/util/EchoHealthIndicator.java
new file mode 100644 (file)
index 0000000..781c7a9
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * ============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.util;
+
+import org.springframework.boot.actuate.health.Health;
+import org.springframework.boot.actuate.health.HealthIndicator;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+
+import lombok.RequiredArgsConstructor;
+
+@Component
+@RequiredArgsConstructor 
+@ConditionalOnProperty(name = "aai.actuator.echo.enabled", havingValue = "true")
+public class EchoHealthIndicator implements HealthIndicator {
+
+  private final AaiGraphChecker aaiGraphChecker;
+
+       @Override
+       public Health health() {
+    return healthy()
+      ? Health.up().build()
+      : Health.down().build();
+       }
+
+       private boolean healthy() {
+    return aaiGraphChecker.isAaiGraphDbAvailable();
+       }
+
+}
index b033473..ebe2356 100644 (file)
@@ -1,14 +1,13 @@
-/**
+/*
  * ============LICENSE_START=======================================================
- * org.onap.aai
- * ================================================================================
- * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2022 Bell Canada
+ * Modification Copyright (C) 2022 Deutsche Telekom SA
  * ================================================================================
  * 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
+ * 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,
  */
 package org.onap.aai.rest.util;
 
-import org.onap.aai.exceptions.AAIException;
-import org.onap.aai.logging.ErrorLogHelper;
-import org.onap.aai.restcore.RESTAPI;
-import org.springframework.stereotype.Component;
+import java.util.ArrayList;
+import java.util.HashMap;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.GET;
@@ -34,89 +31,87 @@ import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
-import java.util.ArrayList;
-import java.util.HashMap;
 
-/**
- * The Class EchoResponse.
- */
+import org.onap.aai.exceptions.AAIException;
+import org.onap.aai.logging.ErrorLogHelper;
+import org.onap.aai.restcore.RESTAPI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
 @Component
 @Path("/util")
 public class EchoResponse extends RESTAPI {
-       
-       protected static String authPolicyFunctionName = "util";
-               
-       public static final String echoPath = "/util/echo";
+    private static final Logger LOGGER = LoggerFactory.getLogger(EchoResponse.class);
+
+    public static final String echoPath = "/util/echo";
+    private final AaiGraphChecker aaiGraphChecker;
+    
+    private static final String UP_RESPONSE="{\"status\":\"UP\",\"groups\":[\"liveness\",\"readiness\"]}";
+   
+  
+    @Autowired
+    public EchoResponse(AaiGraphChecker aaiGraphChecker ) {
+        this.aaiGraphChecker = aaiGraphChecker;
+    }
+
+    @GET
+    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+    @Path("/echo")
+    public Response echoResult(@Context HttpHeaders headers, @Context HttpServletRequest req,
+                               @QueryParam("action") String myAction) {
+        Response response;
+
+        String fromAppId;
+        String transId;
+        try {
+            fromAppId = getFromAppId(headers);
+            transId = getTransId(headers);
+        } catch (AAIException e) {
+            ArrayList<String> templateVars = new ArrayList<>();
+            templateVars.add("PUT uebProvider");
+            templateVars.add("addTopic");
+            return Response
+                    .status(e.getErrorObject().getHTTPResponseCode())
+                    .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, templateVars))
+                    .build();
+        }
 
-       /**
-        * Simple health-check API that echos back the X-FromAppId and X-TransactionId to clients.
-        * If there is a query string, a transaction gets logged into hbase, proving the application is connected to the data store.
-        * If there is no query string, no transacction logging is done to hbase.
-        *
-        * @param headers the headers
-        * @param req the req
-        * @param myAction if exists will cause transaction to be logged to hbase
-        * @return the response
-        */
-       @GET
-       @Produces( { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-       @Path("/echo")
-       public Response echoResult(@Context HttpHeaders headers, @Context HttpServletRequest req,
-                       @QueryParam("action") String myAction) {
-               Response response = null;
-               
-               AAIException ex = null;
-               String fromAppId = null;
-               String transId = null;
-               
-               try { 
-                       fromAppId = getFromAppId(headers );
-                       transId = getTransId(headers);
-               } catch (AAIException e) { 
-                       ArrayList<String> templateVars = new ArrayList<String>();
-                       templateVars.add("PUT uebProvider");
-                       templateVars.add("addTopic");
-                       return Response
-                                       .status(e.getErrorObject().getHTTPResponseCode())
-                                       .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, templateVars))
-                                       .build();
-               }
-               
-               try {
-                       
-                       HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<AAIException, ArrayList<String>>();
-                                       
-                       ArrayList<String> templateVars = new ArrayList<String>();
-                       templateVars.add(fromAppId);
-                       templateVars.add(transId);
-               
-                       exceptionList.put(new AAIException("AAI_0002", "OK"), templateVars);
-                               
-                       response = Response.status(Status.OK)
-                                       .entity(ErrorLogHelper.getRESTAPIInfoResponse(new ArrayList<>(headers.getAcceptableMediaTypes())
-                                                       , exceptionList))
-                                                       .build();
-                       
-               } catch (Exception e) {
-                       ex = new AAIException("AAI_4000", e);
-                       ArrayList<String> templateVars = new ArrayList<String>();
-                       templateVars.add(Action.GET.name());
-                       templateVars.add(fromAppId +" "+transId);
+        ArrayList<String> templateVars = new ArrayList<>();
+        templateVars.add(fromAppId);
+        templateVars.add(transId);
 
-                       response = Response
-                                       .status(Status.INTERNAL_SERVER_ERROR)
-                                       .entity(ErrorLogHelper.getRESTAPIErrorResponse(
-                                                       new ArrayList<>(headers.getAcceptableMediaTypes()), ex,
-                                                       templateVars)).build();
+        try {
+            if (myAction != null) {
+                LOGGER.info("Checking Database Connectivity...!");
+                if (!aaiGraphChecker.isAaiGraphDbAvailable()) {
+                    throw new AAIException("AAI_5105", "Error establishing a database connection");
+                }
+                return generateSuccessResponse();
+            }
+            return generateSuccessResponse();
 
-               } finally {
-                       if (ex != null) {
-                               ErrorLogHelper.logException(ex);
-                       }
+        } catch (AAIException aaiException) {
+               ErrorLogHelper.logException(aaiException);
+            return generateFailureResponse(headers, templateVars, aaiException);
+        } catch (Exception e) {
+            AAIException aaiException = new AAIException("AAI_4000", e);
+            ErrorLogHelper.logException(aaiException);
+            return generateFailureResponse(headers, templateVars, aaiException);
+        }
+    }
 
-               }
-               
-               return response;
-       }
+    private Response generateSuccessResponse() {
+       return Response.status(Status.OK)
+                       .entity(UP_RESPONSE)
+                       .build();
+    }
 
+    private Response generateFailureResponse(HttpHeaders headers, ArrayList<String> templateVariables,
+               AAIException aaiException) {
+       return Response.status(aaiException.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
+                       .getRESTAPIErrorResponseWithLogging(headers.getAcceptableMediaTypes(), aaiException, templateVariables))
+                       .build();
+    }
 }
index 1eb0bed..47ab0bc 100644 (file)
@@ -98,7 +98,15 @@ aperture.service.timeout-in-milliseconds=300000
 #To Expose the Prometheus scraping endpoint
 management.server.port=8448
 #To Enable Actuator Endpoint, you can override this to True in OOM charts
-management.endpoints.enabled-by-default=false
+management.endpoints.enabled-by-default=true
 #To Enable Actuator Endpoint, you can override this in OOM Charts
-#management.endpoints.web.exposure.include=info, health, loggers, prometheus
+management.endpoints.web.exposure.include=info, health, loggers, prometheus
 management.metrics.web.server.auto-time-requests=false
+
+# If true, the actuator health check will be overriden
+# to use the AaiGraphChecker check instead
+# this does the same as the /echo endpoint,
+# but doesn't show up in micrometer metrics
+aai.actuator.echo.enabled=false
+
+
diff --git a/src/test/java/org/onap/aai/config/WebClientConfiguration.java b/src/test/java/org/onap/aai/config/WebClientConfiguration.java
new file mode 100644 (file)
index 0000000..c76b9b1
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * ============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.config;
+
+import java.time.Duration;
+import java.util.Collections;
+
+import org.onap.aai.setup.SchemaVersions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.Primary;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+@TestConfiguration
+public class WebClientConfiguration {
+
+  @Lazy
+  @Bean
+  WebTestClient mgmtClient(@Value("${local.management.port}") int port) {
+    return WebTestClient.bindToServer()
+      .baseUrl("http://localhost:" + port)
+      .responseTimeout(Duration.ofSeconds(300))
+      .defaultHeaders(headers -> {
+        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+      })
+      .build();
+  }
+}
diff --git a/src/test/java/org/onap/aai/rest/util/EchoHealthIndicatorTest.java b/src/test/java/org/onap/aai/rest/util/EchoHealthIndicatorTest.java
new file mode 100644 (file)
index 0000000..7efc0db
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ * ============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.util;
+
+import static org.mockito.Mockito.when;
+
+import org.junit.jupiter.api.Test;
+import org.onap.aai.config.WebClientConfiguration;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+
+@Import(WebClientConfiguration.class)
+@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT, properties = "server.port=8080")
+@TestPropertySource(properties = {"aai.actuator.echo.enabled=true",
+                                                                       "server.ssl.enabled=false"})
+public class EchoHealthIndicatorTest {
+
+  @Autowired
+  @Qualifier("mgmtClient")
+  WebTestClient webClient;
+
+  @MockBean private AaiGraphChecker aaiGraphChecker;
+
+  @Test
+  public void thatActuatorCheckIsHealthy() {
+    when(aaiGraphChecker.isAaiGraphDbAvailable()).thenReturn(true);
+
+    webClient.get()
+      .uri("/actuator/health")
+      .exchange()
+      .expectStatus()
+      .isOk();
+  }
+
+  @Test
+  public void thatActuatorCheckIsUnhealthy() {
+    when(aaiGraphChecker.isAaiGraphDbAvailable()).thenReturn(false);
+
+    webClient.get()
+      .uri("/actuator/health")
+      .exchange()
+      .expectStatus()
+      .is5xxServerError();
+  }
+}
diff --git a/src/test/java/org/onap/aai/rest/util/EchoResponseTest.java b/src/test/java/org/onap/aai/rest/util/EchoResponseTest.java
new file mode 100644 (file)
index 0000000..95e1230
--- /dev/null
@@ -0,0 +1,166 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. 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.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyObject;import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.onap.aai.AAISetup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+
+@ContextConfiguration(classes = {AaiGraphChecker.class})
+public class EchoResponseTest extends AAISetup {
+
+    protected static final MediaType APPLICATION_JSON = MediaType.valueOf("application/json");
+
+    private static final Set<Integer> VALID_HTTP_STATUS_CODES = new HashSet<>();
+
+    static {
+        VALID_HTTP_STATUS_CODES.add(200);
+        VALID_HTTP_STATUS_CODES.add(201);
+        VALID_HTTP_STATUS_CODES.add(204);
+    }
+
+    private EchoResponse echoResponse;
+
+    private final AaiGraphChecker aaiGraphCheckerMock = mock(AaiGraphChecker.class);
+
+    private HttpHeaders httpHeaders;
+
+    private UriInfo uriInfo;
+
+    private MultivaluedMap<String, String> headersMultiMap;
+    private MultivaluedMap<String, String> queryParameters;
+
+    private List<String> aaiRequestContextList;
+
+    private List<MediaType> outputMediaTypes;
+
+    private static final Logger logger = LoggerFactory.getLogger(EchoResponseTest.class.getName());
+
+    @BeforeEach
+    public void setup(){
+        logger.info("Starting the setup for the integration tests of Rest Endpoints");
+
+        echoResponse  = new EchoResponse(aaiGraphCheckerMock);
+        httpHeaders         = mock(HttpHeaders.class);
+        uriInfo             = mock(UriInfo.class);
+
+        headersMultiMap     = new MultivaluedHashMap<>();
+        queryParameters     = Mockito.spy(new MultivaluedHashMap<>());
+
+        headersMultiMap.add("X-FromAppId", "JUNIT");
+        headersMultiMap.add("X-TransactionId", UUID.randomUUID().toString());
+        headersMultiMap.add("Real-Time", "true");
+        headersMultiMap.add("Accept", "application/json");
+        headersMultiMap.add("aai-request-context", "");
+
+        outputMediaTypes = new ArrayList<>();
+        outputMediaTypes.add(APPLICATION_JSON);
+
+        aaiRequestContextList = new ArrayList<>();
+        aaiRequestContextList.add("");
+
+        when(httpHeaders.getAcceptableMediaTypes()).thenReturn(outputMediaTypes);
+        when(httpHeaders.getRequestHeaders()).thenReturn(headersMultiMap);
+        when(httpHeaders.getRequestHeader("X-FromAppId")).thenReturn(Arrays.asList("JUNIT"));
+        when(httpHeaders.getRequestHeader("X-TransactionId")).thenReturn(Arrays.asList("JUNIT"));
+
+        when(httpHeaders.getRequestHeader("aai-request-context")).thenReturn(aaiRequestContextList);
+
+
+        when(uriInfo.getQueryParameters()).thenReturn(queryParameters);
+        when(uriInfo.getQueryParameters(false)).thenReturn(queryParameters);
+
+        // TODO - Check if this is valid since RemoveDME2QueryParameters seems to be very unreasonable
+        Mockito.doReturn(null).when(queryParameters).remove(anyObject());
+
+        when(httpHeaders.getMediaType()).thenReturn(APPLICATION_JSON);
+    }
+
+    @Test
+    public void testEchoResultWhenValidHeaders() throws Exception {
+
+        Response response = echoResponse.echoResult(httpHeaders, null, null);
+
+        assertNotNull(response);
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+    }
+
+    @Test
+    public void testEchoResultWhenActionIsProvidedDbAvailable() throws Exception {
+        when(aaiGraphCheckerMock.isAaiGraphDbAvailable()).thenReturn(true);
+        Response response = echoResponse.echoResult(httpHeaders, null, "myAction");
+
+        assertNotNull(response);
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+    }
+
+    @Test
+    public void testEchoResultWhenActionIsProvidedDbNotAvailable() throws Exception {
+        when(aaiGraphCheckerMock.isAaiGraphDbAvailable()).thenReturn(false);
+        Response response = echoResponse.echoResult(httpHeaders, null, "myAction");
+
+        assertNotNull(response);
+        assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
+    }
+
+    @Test
+    public void testEchoResultWhenInValidHeadersThrowsBadRequest() throws Exception {
+
+        httpHeaders = mock(HttpHeaders.class);
+        Response response = echoResponse.echoResult(httpHeaders, null, null);
+
+        assertNotNull(response);
+        assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+    }
+
+    
+    @Test
+    public void testCheckDbNowAction_Unknown() {
+       when(aaiGraphCheckerMock.isAaiGraphDbAvailable()).thenReturn(null);
+        Response response = echoResponse.echoResult(httpHeaders, null, "myAction");
+        // Verify
+        assertNotNull(response);
+        assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
+    }
+}
\ No newline at end of file