From 8e6846a83c9e8c0249584e52d72b9e9ffa64d786 Mon Sep 17 00:00:00 2001
From: Pavel Bachyla <PBachyla@slb.com>
Date: Wed, 28 Oct 2020 11:15:08 +0000
Subject: [PATCH] Merged PR 215011: Import from OSDU

Related work items: #575714
---
 .gitlab-ci.yml                                |  13 +
 README.md                                     |   5 +
 devops/Jenkinsfile                            |  85 ++++++
 .../osdu/indexer/api/CleanupIndiciesApi.java  |  81 ++++++
 .../osdu/indexer/service/IndexerService.java  |   3 +
 .../indexer/service/IndexerServiceImpl.java   |  27 ++
 .../indexer/service/StorageServiceImpl.java   |   2 +-
 .../indexer/util/ElasticClientHandler.java    | 258 ++++++++++--------
 .../indexer/api/CleanupIndiciesApiTest.java   |  84 ++++++
 .../service/IndexerServiceImplTest.java       |  56 ++++
 .../src/test/resources/application.properties |   1 +
 lombok.config                                 |   2 +
 provider/indexer-gcp/README.md                | 228 ++++++++++++++++
 provider/indexer-gcp/pom.xml                  |  23 +-
 .../indexer-gcp/src/main/appengine/app.yaml   |   1 +
 .../osdu/indexer/di/AppengineLogFactory.java  |  29 --
 .../di/DatastoreCredentialsCacheFactory.java  |  22 ++
 .../opengroup/osdu/indexer/kms/KmsClient.java | 102 -------
 .../persistence/DatastoreCredential.java      | 109 --------
 .../indexer/persistence/DatastoreFactory.java |  67 -----
 .../ElasticRepositoryDatastore.java           |   3 +-
 .../resources/application-kuber.properties    |   2 +-
 .../resources/application-testing.properties  |   2 +-
 .../src/main/resources/application.properties |   2 +
 provider/indexer-ibm/pom.xml                  |   2 +-
 .../indexer/ibm/security/SecurityConfig.java  |   2 +
 .../ibm/util/GlobalExceptionMapper.java       |  83 ++++++
 .../ibm/util/IndexerQueueTaskBuilderIbm.java  |  86 ++++++
 .../indexer/ibm/util/RequestInfoImpl.java     |  25 +-
 .../org/opengroup/osdu/util/HTTPClient.java   |   1 +
 .../features/indexrecord/IndexRecord.feature  |   8 +-
 testing/indexer-test-ibm/pom.xml              |   2 +-
 .../src/main/resources/apikey.feature         |  10 +
 .../index/record/RunTest.java                 |   2 +-
 .../step_definitions/index/record/Steps.java  |  60 +++-
 35 files changed, 1055 insertions(+), 433 deletions(-)
 create mode 100644 devops/Jenkinsfile
 create mode 100644 indexer-core/src/main/java/org/opengroup/osdu/indexer/api/CleanupIndiciesApi.java
 create mode 100644 indexer-core/src/test/java/org/opengroup/osdu/indexer/api/CleanupIndiciesApiTest.java
 create mode 100644 indexer-core/src/test/java/org/opengroup/osdu/indexer/service/IndexerServiceImplTest.java
 create mode 100644 indexer-core/src/test/resources/application.properties
 create mode 100644 lombok.config
 create mode 100644 provider/indexer-gcp/README.md
 delete mode 100644 provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/di/AppengineLogFactory.java
 create mode 100644 provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/di/DatastoreCredentialsCacheFactory.java
 delete mode 100644 provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/kms/KmsClient.java
 delete mode 100644 provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/DatastoreCredential.java
 delete mode 100644 provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/DatastoreFactory.java
 create mode 100644 provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/GlobalExceptionMapper.java
 create mode 100644 provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/IndexerQueueTaskBuilderIbm.java
 create mode 100644 testing/indexer-test-ibm/src/main/resources/apikey.feature

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 726e9af5c..f03a7d498 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -14,6 +14,14 @@ variables:
   GCP_DOMAIN: cloud.slb-ds.com
   GCP_STORAGE_URL: https://osdu-indexer-dot-opendes.appspot.com/api/storage/v2/
 
+  OSDU_GCP_BUILD_SUBDIR: provider/indexer-gcp
+  OSDU_GCP_INT_TEST_SUBDIR: testing/indexer-test-gcp
+  OSDU_GCP_APPLICATION_NAME: os-indexer
+  OSDU_GCP_PROJECT: nice-etching-277309
+  OSDU_GCP_TENANT_NAME: osdu
+  OSDU_GCP_STORAGE_SCHEMA_HOST: https://os-storage-dot-nice-etching-277309.uc.r.appspot.com/api/storage/v2/schemas
+  OSDU_SECURITY_HTTPS_CERTIFICATE_TRUST: 'true'
+
   IBM_BUILD_SUBDIR: provider/indexer-ibm
   IBM_INT_TEST_SUBDIR: testing/indexer-test-ibm
 
@@ -46,5 +54,10 @@ include:
   - project: "osdu/platform/ci-cd-pipelines"
     file: "publishing/pages.yml"
 
+  - project: 'osdu/platform/ci-cd-pipelines'
+    ref: "master"
+    file: 'cloud-providers/osdu-gcp.yml'
+
+
 aws-test-java:
   tags: ['aws-internal-test']
diff --git a/README.md b/README.md
index 596f3c7ea..5e081a10b 100644
--- a/README.md
+++ b/README.md
@@ -4,3 +4,8 @@
 
 The [os-indexer-azure README.md](./provider/indexer-azure/README.md) has all the information needed to get started
 running the `os-indexer` Azure implementation
+
+## GCP Implementation
+
+All documentation for the GCP implementation of `os-indexer` lives [here](./provider/indexer-gcp/README.md)
+
diff --git a/devops/Jenkinsfile b/devops/Jenkinsfile
new file mode 100644
index 000000000..84a24dd3c
--- /dev/null
+++ b/devops/Jenkinsfile
@@ -0,0 +1,85 @@
+pipeline {
+ agent {
+   kubernetes {
+   cloud 'openshift'
+     label 'maven-openjdk18'
+	 yaml """
+spec:
+  containers:
+  - name: jnlp
+    image: quay.io/openshift/origin-jenkins-agent-maven:v4.0.0
+    volumeMounts:
+    - mountPath: "/tmp"
+      name: "workspace-volume"
+      readOnly: false
+    workingDir: "/tmp"
+    securityContext:
+      privileged: false	
+    tty: false
+    resources:
+      limits: 
+        cpu: 200m
+        memory: 2Gi
+      requests: 
+        cpu: 200m
+        memory: 2Gi    
+  restartPolicy: "Never"
+"""		 
+   }
+ }
+
+ environment { 
+  
+ //Cluster environment variable(CLS_ENV). Like QA, DEV, PERF, PROD etc.
+ CLS_ENV = "dev"
+ 
+ //Service variable(CORE_SERVICE). Like indexer, search, delivery, storage, legal etc.
+ CORE_SERVICE = "indexer"
+
+ //GitHub repo URL credential ID for Environment variable files which saved as Secure text in Jenkins Credential.
+ GIT_ENV_VAR_PATH_URL =  credentials('GitRepo-URL-For-Environment-variables') 
+ 
+ //Personal token variable ID which saved as Secure text in Jenkins Credential. Like: GitHub-PRIVATE-TOKEN.
+ PRIVATE_TOKEN  = credentials('GitHub-PRIVATE-TOKEN')
+ 
+ def runShell =  sh (returnStdout: true, script: "curl --header 'PRIVATE-TOKEN: $PRIVATE_TOKEN' ''$GIT_ENV_VAR_PATH_URL'%2F'$CORE_SERVICE'_'$CLS_ENV'_env.json/raw?ref=master' -s -o env.json")
+
+ }
+
+   stages {
+      stage('Integration_test') {
+          environment {
+            def readContent = readJSON file: 'env.json'
+
+			AUTH_USER_ACCESS = "${readContent['AUTH_USER_ACCESS']}"
+			AUTH_USER_ACCESS_PASSWORD = "${readContent['AUTH_USER_ACCESS_PASSWORD']}"
+			DEFAULT_DATA_PARTITION_ID_TENANT1 = "${readContent['DEFAULT_DATA_PARTITION_ID_TENANT1']}"
+			DEFAULT_DATA_PARTITION_ID_TENANT2 = "${readContent['DEFAULT_DATA_PARTITION_ID_TENANT2']}"
+			ELASTIC_HOST = "${readContent['ELASTIC_HOST']}"
+			ELASTIC_PASSWORD = "${readContent['ELASTIC_PASSWORD']}"
+			ELASTIC_PORT = "${readContent['ELASTIC_PORT']}"
+			ELASTIC_USER_NAME = "${readContent['ELASTIC_USER_NAME']}"
+			ENTITLEMENTS_DOMAIN = "${readContent['ENTITLEMENTS_DOMAIN']}"
+			KEYCLOAK_CLIENT_ID = "${readContent['KEYCLOAK_CLIENT_ID']}"
+			KEYCLOAK_CLIENT_SECRET = "${readContent['KEYCLOAK_CLIENT_SECRET']}"
+			KEYCLOAK_REALM = "${readContent['KEYCLOAK_REALM']}"
+			KEYCLOAK_URL = "${readContent['KEYCLOAK_URL']}"
+			LEGAL_TAG = "${readContent['LEGAL_TAG']}"
+			OTHER_RELEVANT_DATA_COUNTRIES = "${readContent['OTHER_RELEVANT_DATA_COUNTRIES']}"
+			STORAGE_HOST = "${readContent['STORAGE_HOST']}"
+			SEARCH_HOST = "${readContent['SEARCH_HOST']}"
+			LEGAL_HOST = "${readContent['LEGAL_HOST']}"
+			INDEXER_HOST = "${readContent['INDEXER_HOST']}"
+			ENTITLEMENT_URL = "${readContent['ENTITLEMENT_URL']}"
+
+          }  
+            steps {
+               script {
+                  sh 'mvn -f testing/indexer-test-ibm/pom.xml test'
+               }
+            }
+       }
+    }
+
+
+}
\ No newline at end of file
diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/api/CleanupIndiciesApi.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/api/CleanupIndiciesApi.java
new file mode 100644
index 000000000..347caf52d
--- /dev/null
+++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/api/CleanupIndiciesApi.java
@@ -0,0 +1,81 @@
+// Copyright 2017-2019, Schlumberger
+//
+// 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.
+
+package org.opengroup.osdu.indexer.api;
+
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import com.google.gson.JsonParseException;
+import java.lang.reflect.Type;
+import java.util.List;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import lombok.extern.java.Log;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.core.common.model.indexer.RecordInfo;
+import org.opengroup.osdu.core.common.model.search.RecordChangedMessages;
+import org.opengroup.osdu.core.common.model.search.SearchServiceRole;
+import org.opengroup.osdu.indexer.SwaggerDoc;
+import org.opengroup.osdu.indexer.service.IndexerService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.context.annotation.RequestScope;
+
+@Log
+@RestController
+@RequestScope
+public class CleanupIndiciesApi {
+
+  @Autowired
+  private IndexerService indexerService;
+
+  @PostMapping(path = "/index-cleanup", consumes = "application/json")
+  @PreAuthorize("@authorizationFilter.hasPermission('" + SearchServiceRole.ADMIN + "')")
+  public ResponseEntity cleanupIndices(@NotNull(message = SwaggerDoc.REQUEST_VALIDATION_NOT_NULL_BODY)
+                                         @Valid @RequestBody RecordChangedMessages message) {
+    if (message == null) {
+      throw new AppException(HttpStatus.BAD_REQUEST.value(), "Request body is null",
+          SwaggerDoc.REQUEST_VALIDATION_NOT_NULL_BODY);
+    }
+
+    if (message.missingAccountId()) {
+      throw new AppException(HttpStatus.BAD_REQUEST.value(), "Invalid tenant",
+          String.format("Required header: '%s' not found", DpsHeaders.DATA_PARTITION_ID));
+    }
+    try {
+      Type listType = new TypeToken<List<RecordInfo>>() {
+      }.getType();
+      List<RecordInfo> recordInfos = new Gson().fromJson(message.getData(), listType);
+
+      if (recordInfos.isEmpty()) {
+        log.info("none of record-change message can be deserialized");
+        return new ResponseEntity(HttpStatus.OK);
+      }
+      indexerService.processSchemaMessages(recordInfos);
+      return new ResponseEntity(HttpStatus.OK);
+    } catch (AppException e) {
+      throw e;
+    } catch (JsonParseException e) {
+      throw new AppException(HttpStatus.BAD_REQUEST.value(), "Request payload parsing error", "Unable to parse request payload.", e);
+    } catch (Exception e) {
+      throw new AppException(HttpStatus.BAD_REQUEST.value(), "Unknown error", "An unknown error has occurred.", e);
+    }
+  }
+}
diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerService.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerService.java
index 4ae323c76..8feadb3d3 100644
--- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerService.java
+++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerService.java
@@ -14,6 +14,7 @@
 
 package org.opengroup.osdu.indexer.service;
 
+import java.io.IOException;
 import java.util.List;
 
 import org.opengroup.osdu.core.common.model.indexer.JobStatus;
@@ -24,4 +25,6 @@ public interface IndexerService {
 
     JobStatus processRecordChangedMessages(RecordChangedMessages recordChangedMessages, List<RecordInfo> recordInfos) throws Exception;
 
+    void processSchemaMessages(List<RecordInfo> recordInfos) throws IOException;
+
 }
\ No newline at end of file
diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerServiceImpl.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerServiceImpl.java
index 0fa068296..36f91af24 100644
--- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerServiceImpl.java
+++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/IndexerServiceImpl.java
@@ -153,6 +153,33 @@ public class IndexerServiceImpl implements IndexerService {
         return jobStatus;
     }
 
+    @Override
+    public void processSchemaMessages(List<RecordInfo> recordInfos) throws IOException {
+        Map<String, OperationType> schemaMsgs = RecordInfo.getSchemaMsgs(recordInfos);
+        if (schemaMsgs != null && !schemaMsgs.isEmpty()) {
+            try (RestHighLevelClient restClient = elasticClientHandler.createRestClient()) {
+                schemaMsgs.entrySet().forEach(msg -> {
+                    try {
+                        processSchemaEvents(restClient, msg);
+                    } catch (IOException | ElasticsearchStatusException e) {
+                        throw new AppException(org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR.value(), "unable to process schema delete", e.getMessage());
+                    }
+                });
+            }
+        }
+    }
+
+    private void processSchemaEvents(RestHighLevelClient restClient,
+                                     Map.Entry<String, OperationType> msg) throws IOException, ElasticsearchStatusException {
+        String kind = msg.getKey();
+        String index = elasticIndexNameResolver.getIndexNameFromKind(kind);
+
+        boolean indexExist = indicesService.isIndexExist(restClient, index);
+        if (indexExist && msg.getValue() == OperationType.purge_schema) {
+            indicesService.deleteIndex(restClient, index);
+        }
+    }
+
     private List<String> processUpsertRecords(Map<String, Map<String, OperationType>> upsertRecordMap) throws Exception {
         // get schema for kind
         Map<String, IndexSchema> schemas = this.getSchema(upsertRecordMap);
diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/StorageServiceImpl.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/StorageServiceImpl.java
index 4fb392ded..9b287fd59 100644
--- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/StorageServiceImpl.java
+++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/service/StorageServiceImpl.java
@@ -202,7 +202,7 @@ public class StorageServiceImpl implements StorageService {
         FetchServiceHttpRequest request = FetchServiceHttpRequest.builder()
                 .httpMethod(HttpMethods.GET)
                 .headers(this.requestInfo.getHeadersMap())
-                .url(Config.getStorageQueryRecordHostUrl())
+                .url(STORAGE_QUERY_RECORD_HOST)
                 .queryParams(queryParams)
                 .build();
 
diff --git a/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/ElasticClientHandler.java b/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/ElasticClientHandler.java
index 100796717..7c40de073 100644
--- a/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/ElasticClientHandler.java
+++ b/indexer-core/src/main/java/org/opengroup/osdu/indexer/util/ElasticClientHandler.java
@@ -1,108 +1,152 @@
-// Copyright 2017-2019, Schlumberger
-//
-// 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.
-
-package org.opengroup.osdu.indexer.util;
-
-import org.apache.http.Header;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpStatus;
-import org.apache.http.message.BasicHeader;
-import org.elasticsearch.client.RestClient;
-import org.elasticsearch.client.RestClientBuilder;
-import org.elasticsearch.client.RestHighLevelClient;
-import org.opengroup.osdu.core.common.model.http.AppException;
-import org.opengroup.osdu.core.common.model.search.ClusterSettings;
-import org.opengroup.osdu.core.common.model.indexer.IElasticSettingService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import java.util.Base64;
-
-@Component
-public class ElasticClientHandler {
-
-    // Elastic cluster Rest client settings
-    private static final int CLOUD_REST_CLIENT_PORT = 9243;
-    private static final int REST_CLIENT_CONNECT_TIMEOUT = 60000;
-    private static final int REST_CLIENT_SOCKET_TIMEOUT = 60000;
-    private static final int REST_CLIENT_RETRY_TIMEOUT = 60000;
-
-   @Autowired
-    private IElasticSettingService elasticSettingService;
-
-    public RestHighLevelClient createRestClient() {
-        return getCloudRestClient(elasticSettingService.getElasticClusterInformation());
-    }
-    // TODO: Remove this temporary implementation when ECE CCS is utilized
-    public RestHighLevelClient createRestClient(final ClusterSettings clusterSettings) {
-        return getCloudRestClient(clusterSettings);
-    }
-
-    private RestHighLevelClient getCloudRestClient(final ClusterSettings clusterSettings) {
-
-        String cluster = null;
-        String host = null;
-        int port = CLOUD_REST_CLIENT_PORT;
-        String protocolScheme = "https";
-        String tls = "true";
-
-        try {
-            cluster = clusterSettings.getHost();
-            host = clusterSettings.getHost();
-            port = clusterSettings.getPort();
-            if(!clusterSettings.isHttps()){
-                protocolScheme = "http";
-            }
-
-            if(!clusterSettings.isTls()){
-                tls = "false";
-            }
-            String basicEncoded = Base64.getEncoder().encodeToString(clusterSettings.getUserNameAndPassword().getBytes());
-            String basicAuthenticationHeaderVal = String.format("Basic %s", basicEncoded);
-
-            RestClientBuilder builder = createClientBuilder(host, basicAuthenticationHeaderVal, port, protocolScheme, tls);
-
-            return new RestHighLevelClient(builder);
-        } catch (AppException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new AppException(
-                    HttpStatus.SC_INTERNAL_SERVER_ERROR,
-                    "search client error",
-                    "error creating search client",
-                    String.format("Elastic client connection params, cluster: %s, host: %s, port: %s", cluster, host, port),
-                    e);
-        }
-    }
-
-    public RestClientBuilder createClientBuilder(String host, String basicAuthenticationHeaderVal, int port, String protocolScheme, String tls) {
-        RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, protocolScheme));
-        builder.setRequestConfigCallback(requestConfigBuilder -> requestConfigBuilder.setConnectTimeout(REST_CLIENT_CONNECT_TIMEOUT)
-                .setSocketTimeout(REST_CLIENT_SOCKET_TIMEOUT));
-        builder.setMaxRetryTimeoutMillis(REST_CLIENT_RETRY_TIMEOUT);
-
-        Header[] defaultHeaders = new Header[]{
-                new BasicHeader("client.transport.nodes_sampler_interval", "30s"),
-                new BasicHeader("client.transport.ping_timeout", "30s"),
-                new BasicHeader("client.transport.sniff", "false"),
-                new BasicHeader("request.headers.X-Found-Cluster", host),
-                new BasicHeader("cluster.name", host),
-                new BasicHeader("xpack.security.transport.ssl.enabled", tls),
-                new BasicHeader("Authorization", basicAuthenticationHeaderVal),
-        };
-
-        builder.setDefaultHeaders(defaultHeaders);
-        return builder;
-    }
+package org.opengroup.osdu.indexer.util;
+
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+import javax.net.ssl.SSLContext;
+import lombok.extern.java.Log;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpStatus;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestClientBuilder;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.opengroup.osdu.core.common.model.indexer.IElasticSettingService;
+import org.opengroup.osdu.core.common.model.search.ClusterSettings;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+@Log
+public class ElasticClientHandler {
+
+  // Elastic cluster Rest client settings
+  private static final int CLOUD_REST_CLIENT_PORT = 9243;
+  private static final int REST_CLIENT_CONNECT_TIMEOUT = 60000;
+  private static final int REST_CLIENT_SOCKET_TIMEOUT = 60000;
+  private static final int REST_CLIENT_RETRY_TIMEOUT = 60000;
+
+  @Value("#{new Boolean('${security.https.certificate.trust:false}')}")
+  private Boolean isSecurityHttpsCertificateTrust;
+
+  @Autowired
+  private IElasticSettingService elasticSettingService;
+
+  public RestHighLevelClient createRestClient() {
+    return getCloudRestClient(elasticSettingService.getElasticClusterInformation());
+  }
+
+  // TODO: Remove this temporary implementation when ECE CCS is utilized
+  public RestHighLevelClient createRestClient(final ClusterSettings clusterSettings) {
+    return getCloudRestClient(clusterSettings);
+  }
+
+  private RestHighLevelClient getCloudRestClient(final ClusterSettings clusterSettings) {
+
+    String cluster = null;
+    String host = null;
+    int port = CLOUD_REST_CLIENT_PORT;
+    String protocolScheme = "https";
+    String tls = "true";
+
+    try {
+      cluster = clusterSettings.getHost();
+      host = clusterSettings.getHost();
+      port = clusterSettings.getPort();
+      if (!clusterSettings.isHttps()) {
+        protocolScheme = "http";
+      }
+
+      if (!clusterSettings.isTls()) {
+        tls = "false";
+      }
+      String basicEncoded = Base64
+          .getEncoder().encodeToString(clusterSettings.getUserNameAndPassword().getBytes());
+      String basicAuthenticationHeaderVal = String.format("Basic %s", basicEncoded);
+
+      RestClientBuilder builder = createClientBuilder(host, basicAuthenticationHeaderVal, port,
+          protocolScheme, tls);
+
+      return new RestHighLevelClient(builder);
+    } catch (AppException e) {
+      throw e;
+    } catch (Exception e) {
+      throw new AppException(
+          HttpStatus.SC_INTERNAL_SERVER_ERROR,
+          "search client error",
+          "error creating search client",
+          String
+              .format("Elastic client connection params, cluster: %s, host: %s, port: %s", cluster,
+                  host, port),
+          e);
+    }
+  }
+
+  public RestClientBuilder createClientBuilder(String host, String basicAuthenticationHeaderVal,
+      int port, String protocolScheme, String tls) {
+    RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, protocolScheme));
+    builder.setRequestConfigCallback(
+        requestConfigBuilder -> requestConfigBuilder.setConnectTimeout(REST_CLIENT_CONNECT_TIMEOUT)
+            .setSocketTimeout(REST_CLIENT_SOCKET_TIMEOUT));
+    builder.setMaxRetryTimeoutMillis(REST_CLIENT_RETRY_TIMEOUT);
+
+    Header[] defaultHeaders = new Header[]{
+        new BasicHeader("client.transport.nodes_sampler_interval", "30s"),
+        new BasicHeader("client.transport.ping_timeout", "30s"),
+        new BasicHeader("client.transport.sniff", "false"),
+        new BasicHeader("request.headers.X-Found-Cluster", host),
+        new BasicHeader("cluster.name", host),
+        new BasicHeader("xpack.security.transport.ssl.enabled", tls),
+        new BasicHeader("Authorization", basicAuthenticationHeaderVal),
+    };
+    log.info(String.format(
+        "Elastic client connection uses protocolScheme = %s with a flag "
+            + "'security.https.certificate.trust' = %s",
+        protocolScheme, isSecurityHttpsCertificateTrust));
+    if ("https".equals(protocolScheme) && isSecurityHttpsCertificateTrust) {
+      log.warning("Elastic client connection uses TrustSelfSignedStrategy()");
+      SSLContext sslContext = createSSLContext();
+      builder.setHttpClientConfigCallback(httpClientBuilder ->
+      {
+        HttpAsyncClientBuilder httpAsyncClientBuilder = httpClientBuilder.setSSLContext(sslContext)
+            .setSSLHostnameVerifier(
+                NoopHostnameVerifier.INSTANCE);
+        return httpAsyncClientBuilder;
+      });
+    }
+
+    builder.setDefaultHeaders(defaultHeaders);
+    return builder;
+  }
+
+  private SSLContext createSSLContext() {
+    SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
+    try {
+      sslContextBuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
+      return sslContextBuilder.build();
+    } catch (NoSuchAlgorithmException e) {
+      log.severe(e.getMessage());
+    } catch (KeyStoreException e) {
+      log.severe(e.getMessage());
+    } catch (KeyManagementException e) {
+      log.severe(e.getMessage());
+    }
+    return null;
+  }
+
+  public Boolean isSecurityHttpsCertificateTrust() {
+    return isSecurityHttpsCertificateTrust;
+  }
+
+  public void setSecurityHttpsCertificateTrust(Boolean isSecurityHttpsCertificateTrust) {
+    this.isSecurityHttpsCertificateTrust = isSecurityHttpsCertificateTrust;
+  }
 }
\ No newline at end of file
diff --git a/indexer-core/src/test/java/org/opengroup/osdu/indexer/api/CleanupIndiciesApiTest.java b/indexer-core/src/test/java/org/opengroup/osdu/indexer/api/CleanupIndiciesApiTest.java
new file mode 100644
index 000000000..6a9f2ead5
--- /dev/null
+++ b/indexer-core/src/test/java/org/opengroup/osdu/indexer/api/CleanupIndiciesApiTest.java
@@ -0,0 +1,84 @@
+package org.opengroup.osdu.indexer.api;
+
+import static org.junit.Assert.fail;
+import static org.mockito.MockitoAnnotations.initMocks;
+import com.google.gson.Gson;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.opengroup.osdu.core.common.http.HeadersUtil;
+import org.opengroup.osdu.core.common.model.http.AppException;
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.core.common.model.search.RecordChangedMessages;
+import org.opengroup.osdu.core.common.search.Config;
+import org.opengroup.osdu.indexer.service.IndexerService;
+import org.opengroup.osdu.indexer.util.IndexerQueueTaskBuilder;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({HeadersUtil.class, IndexerQueueTaskBuilder.class, DpsHeaders.class, Config.class})
+public class CleanupIndiciesApiTest {
+
+  private final String messageValid = "{\"data\":\"[{\\\"id\\\":\\\"opendes:welldb:wellbore-d9033ae1-fb15-496c-9ba0-880fd1d2b2cf\\\",\\\"kind\\\":\\\"tenant1:welldb:wellbore:1.0.0\\\",\\\"op\\\":\\\"purge_schema\\\"}]\",\"attributes\":{\"account-id\":\"opendes\",\"correlation-id\":\"b5a281bd-f59d-4db2-9939-b2d85036fc7e\"},\"messageId\":\"75328163778221\",\"publishTime\":\"2018-05-08T21:48:56.131Z\"}";
+  private final String messageEmpty = "{}";
+  private final String messageWithEmptyData = "{\"data\":\"[]\",\"attributes\":{\"account-id\":\"opendes\",\"correlation-id\":\"b5a281bd-f59d-4db2-9939-b2d85036fc7e\"},\"messageId\":\"75328163778221\",\"publishTime\":\"2018-05-08T21:48:56.131Z\"}";
+  private final String messageWithIncorrectJsonFormat = "{\"data\":\"[{}}]\",\"attributes\":{\"account-id\":\"opendes\",\"correlation-id\":\"b5a281bd-f59d-4db2-9939-b2d85036fc7e\"},\"messageId\":\"75328163778221\",\"publishTime\":\"2018-05-08T21:48:56.131Z\"}";
+
+  @InjectMocks
+  private CleanupIndiciesApi sut;
+
+  @Mock
+  private IndexerService indexerService;
+
+  @Before
+  public void setup() {
+    initMocks(this);
+  }
+
+  @Test
+  public void should_return200_given_validMessage_indexCleanupTest() {
+    should_return200_indexerWorkerTest(messageValid);
+  }
+
+  @Test
+  public void should_return200_given_emptyData_indexCleanupTest() {
+    should_return200_indexerWorkerTest(messageWithEmptyData);
+  }
+
+  @Test
+  public void should_return400_given_emptyMessage_indexCleanupTest() {
+    should_return400_indexerWorkerTest(messageEmpty, String.format("Required header: '%s' not found", DpsHeaders.DATA_PARTITION_ID));
+  }
+
+  @Test
+  public void should_return400_given_incorrectJsonFormatMessage_indexWorkerTest() {
+    should_return400_indexerWorkerTest(messageWithIncorrectJsonFormat, "Unable to parse request payload.");
+  }
+
+  private void should_return200_indexerWorkerTest(String message) {
+    ResponseEntity response = this.sut.cleanupIndices(createRecordChangedMessage(message));
+    Assert.assertEquals(HttpStatus.OK.value(), response.getStatusCodeValue());
+  }
+
+  private void should_return400_indexerWorkerTest(String message, String errorMessage) {
+    try {
+      this.sut.cleanupIndices(createRecordChangedMessage(message));
+      fail("Should throw exception");
+    } catch (AppException e) {
+      Assert.assertEquals(HttpStatus.BAD_REQUEST.value(), e.getError().getCode());
+      Assert.assertEquals(errorMessage, e.getError().getMessage());
+    } catch (Exception e) {
+      fail("Should not throw this exception" + e.getMessage());
+    }
+  }
+
+  private RecordChangedMessages createRecordChangedMessage(String message) {
+    return (new Gson()).fromJson(message, RecordChangedMessages.class);
+  }
+}
diff --git a/indexer-core/src/test/java/org/opengroup/osdu/indexer/service/IndexerServiceImplTest.java b/indexer-core/src/test/java/org/opengroup/osdu/indexer/service/IndexerServiceImplTest.java
new file mode 100644
index 000000000..c3c3d3a7a
--- /dev/null
+++ b/indexer-core/src/test/java/org/opengroup/osdu/indexer/service/IndexerServiceImplTest.java
@@ -0,0 +1,56 @@
+package org.opengroup.osdu.indexer.service;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.opengroup.osdu.core.common.model.indexer.RecordInfo;
+import org.opengroup.osdu.core.common.search.ElasticIndexNameResolver;
+import org.opengroup.osdu.core.common.search.IndicesService;
+import org.opengroup.osdu.indexer.util.ElasticClientHandler;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+public class IndexerServiceImplTest {
+
+  @InjectMocks
+  private IndexerServiceImpl indexerService;
+
+  @Mock
+  private ElasticClientHandler elasticClientHandler;
+
+  @Mock
+  private ElasticIndexNameResolver elasticIndexNameResolver;
+
+  @Mock
+  private IndicesService indicesService;
+
+  private List<RecordInfo> recordInfos = new ArrayList<>();
+
+  @Before
+  public void setup() {
+    RecordInfo recordInfo = new RecordInfo();
+    recordInfo.setId("opendes:ds:mytest3-d9033ae1-fb15-496c-9ba0-880fd1d2b2qf");
+    recordInfo.setKind("opendes:ds:mytest2:1.0.0");
+    recordInfo.setOp("purge_schema");
+    recordInfos.add(recordInfo);
+
+    initMocks(this);
+  }
+
+  @Test
+  public void processSchemaMessagesTest() throws Exception {
+    indexerService.processSchemaMessages(recordInfos);
+
+    verify(elasticClientHandler, times(1)).createRestClient();
+    verify(elasticIndexNameResolver, times(1)).getIndexNameFromKind(any());
+    verify(indicesService, times(1)).isIndexExist(any(), any());
+  }
+}
diff --git a/indexer-core/src/test/resources/application.properties b/indexer-core/src/test/resources/application.properties
new file mode 100644
index 000000000..50f201247
--- /dev/null
+++ b/indexer-core/src/test/resources/application.properties
@@ -0,0 +1 @@
+security.https.certificate.trust=false
\ No newline at end of file
diff --git a/lombok.config b/lombok.config
new file mode 100644
index 000000000..a23edb413
--- /dev/null
+++ b/lombok.config
@@ -0,0 +1,2 @@
+config.stopBubbling = true
+lombok.addLombokGeneratedAnnotation = true
\ No newline at end of file
diff --git a/provider/indexer-gcp/README.md b/provider/indexer-gcp/README.md
new file mode 100644
index 000000000..23c482574
--- /dev/null
+++ b/provider/indexer-gcp/README.md
@@ -0,0 +1,228 @@
+# Indexer Service
+os-indexer-gcp is a [Spring Boot](https://spring.io/projects/spring-boot) service that is responsible for indexing Records that enable the `os-search` service to execute OSDU R2 domain searches against Elasticsearch.
+
+## Getting Started
+These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
+
+### Prerequisites
+Pre-requisites
+
+* GCloud SDK with java (latest version)
+* JDK 8
+* Lombok 1.16 or later
+* Maven
+
+### Installation
+In order to run the service locally or remotely, you will need to have the following environment variables defined.
+
+| name | value | description | sensitive? | source |
+| ---  | ---   | ---         | ---        | ---    |
+| `LOG_PREFIX` | `service` | Logging prefix | no | - |
+| `SERVER_SERVLET_CONTEXPATH` | `/api/indexer/v2` | Servlet context path | no | - |
+| `AUTHORIZE_API` | ex `https://entitlements.com/entitlements/v1` | Entitlements API endpoint | no | output of infrastructure deployment |
+| `ENTITLEMENTS_HOST` | ex `https://entitlements.com/entitlements/v1` | Entitlements API endpoint | no | output of infrastructure deployment |
+| `LEGALTAG_API` | ex `https://legal.com/api/legal/v1` | Legal API endpoint | no | output of infrastructure deployment |
+| `INDEXER_HOST` | ex `os-indexer-dot-opendes.appspot.com` | Indexer Host | no | output of infrastructure deployment |
+| `INDEXER_QUEUE_HOST` | ex `https://os-indexer-queue-dot-opendes.appspot.com/_dps/task-handlers/enqueue` | Indexer-Queue API endpoint | no | output of infrastructure deployment |
+| `CRS_API` | ex `https://crs-converter-gae-dot-opendes.appspot.com/api/crs/v1` | CRS API endpoint | no | https://console.cloud.google.com/memorystore/redis/instances |
+| `STORAGE_HOSTNAME` | ex `os-storage-dot-opendes.appspot.com` | Storage Host | no | output of infrastructure deployment |
+| `STORAGE_SCHEMA_HOST` | ex `https://os-storage-dot-opendes.appspot.com/api/storage/v2/schemas` | Storage API endpoint 'schemas' | no | https://console.cloud.google.com/apis/credentials |
+| `STORAGE_QUERY_RECORD_FOR_CONVERSION_HOST` | ex `https://os-storage-dot-opendes.appspot.com/api/storage/v2/query/records:batch` | Storage API endpoint 'records'  | no | https://console.cloud.google.com/iam-admin/serviceaccounts |
+| `REDIS_SEARCH_HOST` | ex `127.0.0.1` | Redis host for search | no | https://console.cloud.google.com/memorystore/redis/instances |
+| `REDIS_GROUP_HOST` | ex `127.0.0.1` | Redis host for groups | no | https://console.cloud.google.com/memorystore/redis/instances |
+| `REDIS_SEARCH_PORT` | ex `6379` | Redis host for search | no | https://console.cloud.google.com/memorystore/redis/instances |
+| `GOOGLE_CLOUD_PROJECT` | ex `opendes` | Google Cloud Project Id| no | output of infrastructure deployment |
+| `GOOGLE_AUDIENCES` | ex `*****.apps.googleusercontent.com` | Client ID for getting access to cloud resources | yes | https://console.cloud.google.com/apis/credentials |
+| `GOOGLE_APPLICATION_CREDENTIALS` | ex `/path/to/directory/service-key.json` | Service account credentials, you only need this if running locally | yes | https://console.cloud.google.com/iam-admin/serviceaccounts |
+| `security.https.certificate.trust` | ex `false` | Elastic client connection uses TrustSelfSignedStrategy(), if it is 'true' | false | output of infrastructure deployment |
+
+
+### Run Locally
+Check that maven is installed:
+
+```bash
+$ mvn --version
+Apache Maven 3.6.0
+Maven home: /usr/share/maven
+Java version: 1.8.0_212, vendor: AdoptOpenJDK, runtime: /usr/lib/jvm/jdk8u212-b04/jre
+...
+```
+
+You may need to configure access to the remote maven repository that holds the OSDU dependencies. This file should live within `~/.mvn/community-maven.settings.xml`:
+
+```bash
+$ cat ~/.m2/settings.xml
+<?xml version="1.0" encoding="UTF-8"?>
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
+    <servers>
+        <server>
+            <id>community-maven-via-private-token</id>
+            <!-- Treat this auth token like a password. Do not share it with anyone, including Microsoft support. -->
+            <!-- The generated token expires on or before 11/14/2019 -->
+             <configuration>
+              <httpHeaders>
+                  <property>
+                      <name>Private-Token</name>
+                      <value>${env.COMMUNITY_MAVEN_TOKEN}</value>
+                  </property>
+              </httpHeaders>
+             </configuration>
+        </server>
+    </servers>
+</settings>
+```
+
+* Update the Google cloud SDK to the latest version:
+
+```bash
+gcloud components update
+```
+* Set Google Project Id:
+
+```bash
+gcloud config set project <YOUR-PROJECT-ID>
+```
+
+* Perform a basic authentication in the selected project:
+
+```bash
+gcloud auth application-default login
+```
+
+* Navigate to indexer service's root folder and run:
+
+```bash
+mvn jetty:run
+## Testing
+* Navigate to indexer service's root folder and run:
+ 
+```bash
+mvn clean install   
+```
+
+* If you wish to see the coverage report then go to testing/target/site/jacoco-aggregate and open index.html
+
+* If you wish to build the project without running tests
+
+```bash
+mvn clean install -DskipTests
+```
+
+After configuring your environment as specified above, you can follow these steps to build and run the application. These steps should be invoked from the *repository root.*
+
+```bash
+cd provider/indexer-gcp/ && mvn spring-boot:run
+```
+
+## Testing
+Navigate to indexer service's root folder and run all the tests:
+
+```bash
+# build + install integration test core
+$ (cd testing/indexer-test-core/ && mvn clean install)
+```
+
+### Running E2E Tests 
+This section describes how to run cloud OSDU E2E tests (testing/integration-tests/indexer-test-gcp).
+
+You will need to have the following environment variables defined.
+
+| name | value | description | sensitive? | source |
+| ---  | ---   | ---         | ---        | ---    |
+| `ENTITLEMENTS_HOST` | ex `https://entitlements.com/entitlements/v1` | Entitlements API endpoint | no | output of infrastructure deployment |
+| `ELASTIC_PASSWORD` | `********` | Password for Elasticsearch | yes | output of infrastructure deployment |
+| `ELASTIC_USER_NAME` | `********` | User name for Elasticsearch | yes | output of infrastructure deployment |
+| `ELASTIC_HOST` | ex `elastic.domain.com` | Host Elasticsearch | yes | output of infrastructure deployment |
+| `ELASTIC_PORT` | ex `9243` | Port Elasticsearch | yes | output of infrastructure deployment |
+| `GCLOUD_PROJECT` | ex `opendes` | Google Cloud Project Id| no | output of infrastructure deployment |
+| `INDEXER_HOST` | ex `https://os-indexer-dot-opendes.appspot.com/api/indexer/v2/` | Indexer API endpoint | no | output of infrastructure deployment |
+| `DATA_GROUP` | `opendes` | The service account to this group and substitute | no | - |
+| `ENTITLEMENTS_DOMAIN` | ex `opendes-gcp.projects.com` | OSDU R2 to run tests under  | no | - |
+| `INTEGRATION_TEST_AUDIENCE` | `********` | client application ID | yes | https://console.cloud.google.com/apis/credentials |
+| `OTHER_RELEVANT_DATA_COUNTRIES` | ex `US` | valid legal tag with a other relevant data countries | no | - |
+| `LEGAL_TAG` | ex `opendes-demo-legaltag` | valid legal tag with a other relevant data countries from `DEFAULT_OTHER_RELEVANT_DATA_COUNTRIES` | no | - |
+| `DEFAULT_DATA_PARTITION_ID_TENANT1` | ex `opendes` | HTTP Header 'Data-Partition-ID'  | no | - |
+| `SEARCH_INTEGRATION_TESTER` | `********` | Service account for API calls. Note: this user must have entitlements configured already | yes | https://console.cloud.google.com/iam-admin/serviceaccounts |
+| `SEARCH_HOST` | ex `http://localhost:8080/api/search/v2/` | Endpoint of search service | no | - |
+| `STORAGE_HOST` | ex `http://os-storage-dot-opendes.appspot.com/api/storage/v2/schemas` | Storage API endpoint | Storage Host | no | output of infrastructure deployment |
+
+**Entitlements configuration for integration accounts**
+
+| INTEGRATION_TESTER | NO_DATA_ACCESS_TESTER | 
+| ---  | ---   |
+| users<br/>service.entitlements.user<br/>service.search.user<br/>data.test1<br/>data.integration.test<br/>users@{tenant1}@{domain}.com |
+
+Execute following command to build code and run all the integration tests:
+
+```bash
+# Note: this assumes that the environment variables for integration tests as outlined
+#       above are already exported in your environment.
+$ (cd testing/indexer-test-gcp/ && mvn clean test)
+```
+
+## Deployment
+
+* Data-Lake Indexer Google Cloud Endpoints on App Engine Flex environment
+  * Edit the app.yaml
+    * Open the [app.yaml](indexer/src/main/appengine/app.yaml) file in editor, and replace the YOUR-PROJECT-ID `GOOGLE_CLOUD_PROJECT` line with Google Cloud Platform project Id. Also update `AUTHORIZE_API`, `CRON_JOB_IP`, `LEGAL_HOSTNAME`, `REGION` and `SECURITY_HTTPS_CERTIFICATE_TRUST` based on your deployment
+ 
+  * Deploy
+    ```sh
+    mvn appengine:deploy -pl org.opengroup.osdu.indexer:indexer -amd
+    ```
+
+  * If you wish to deploy the search service without running tests
+    ```sh
+    mvn appengine:deploy -pl org.opengroup.osdu.indexer:indexer -amd -DskipTests
+    ```
+
+or
+* Google Documentation: https://cloud.google.com/cloud-build/docs/deploying-builds/deploy-appengine
+
+#### Cloud KMS Setup
+
+Enable cloud KMS on master project
+
+Create king ring and key in the ***master project***
+
+```bash
+    gcloud services enable cloudkms.googleapis.com
+    export KEYRING_NAME="csqp"
+    export CRYPTOKEY_NAME="searchService"
+    gcloud kms keyrings create $KEYRING_NAME --location global
+    gcloud kms keys create $CRYPTOKEY_NAME --location global \
+    		--keyring $KEYRING_NAME \
+    		--purpose encryption
+```
+
+Add **Cloud KMS CryptoKey Encrypter/Decrypter** role to the **App Engine default service account** of the master project through IAM - Role tab
+
+Add "Cloud KMS Encrypt/Decrypt" role to the "App Engine default service account" of ***master project***
+
+#### Memory Store (Redis Instance) Setup
+
+Create a new Standard tier Redis instance on the ***service project***
+
+The Redis instance must be created under the same region with the App Engine application which needs to access it.
+
+```bash
+    gcloud beta redis instances create redis-cache-search --size=10 --region=<service-deployment-region> --zone=<service-deployment-zone> --tier=STANDARD
+```
+
+## Licence
+Copyright © Google LLC
+Copyright © EPAM Systems
+ 
+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,
+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.
\ No newline at end of file
diff --git a/provider/indexer-gcp/pom.xml b/provider/indexer-gcp/pom.xml
index 7b936e801..fe4e902e8 100644
--- a/provider/indexer-gcp/pom.xml
+++ b/provider/indexer-gcp/pom.xml
@@ -43,7 +43,7 @@
         <dependency>
             <groupId>org.opengroup.osdu</groupId>
             <artifactId>core-lib-gcp</artifactId>
-            <version>0.1.17</version>
+            <version>0.3.21</version>
         </dependency>
 
         <dependency>
@@ -202,7 +202,26 @@
                   <useSystemClassLoader>false</useSystemClassLoader>
                   <threadCount>1</threadCount>
                 </configuration>
-            </plugin>            
+            </plugin>
+            <plugin>
+				<groupId>org.jacoco</groupId>
+				<artifactId>jacoco-maven-plugin</artifactId>
+				<version>0.7.7.201606060606</version>
+				<executions>
+					<execution>
+						<goals>
+							<goal>prepare-agent</goal>
+						</goals>
+					</execution>
+					<execution>
+						<id>report</id>
+						<phase>prepare-package</phase>
+						<goals>
+							<goal>report</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
         </plugins>
     </build>
 
diff --git a/provider/indexer-gcp/src/main/appengine/app.yaml b/provider/indexer-gcp/src/main/appengine/app.yaml
index dee451574..363b4097e 100644
--- a/provider/indexer-gcp/src/main/appengine/app.yaml
+++ b/provider/indexer-gcp/src/main/appengine/app.yaml
@@ -39,3 +39,4 @@ env_variables:
   LEGAL_HOSTNAME: "LEGAL_HOSTNAME_VAR"
   REGION: "REGION_VAR"
   SPRING_PROFILES_ACTIVE: 'ENVIRONMENT'
+  SECURITY_HTTPS_CERTIFICATE_TRUST: 'SECURITY_HTTPS_CERTIFICATE_TRUST_VAR'
diff --git a/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/di/AppengineLogFactory.java b/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/di/AppengineLogFactory.java
deleted file mode 100644
index 66ae62f7a..000000000
--- a/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/di/AppengineLogFactory.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.opengroup.osdu.indexer.di;
-
-import org.opengroup.osdu.core.common.logging.ILogger;
-import org.opengroup.osdu.core.gcp.logging.logger.AppEngineLoggingProvider;
-
-import org.springframework.beans.factory.FactoryBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.context.annotation.Primary;
-import org.springframework.stereotype.Component;
-
-@ConditionalOnProperty(name="disable.appengine.log.factory", havingValue = "false", matchIfMissing = true )
-@Component
-@Primary
-@Lazy
-public class AppengineLogFactory implements FactoryBean<ILogger> {
-
-    private AppEngineLoggingProvider appEngineLoggingProvider = new AppEngineLoggingProvider();
-
-    @Override
-    public ILogger getObject() throws Exception {
-        return appEngineLoggingProvider.getLogger();
-    }
-
-    @Override
-    public Class<?> getObjectType() {
-        return ILogger.class;
-    }
-}
\ No newline at end of file
diff --git a/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/di/DatastoreCredentialsCacheFactory.java b/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/di/DatastoreCredentialsCacheFactory.java
new file mode 100644
index 000000000..94e9d343b
--- /dev/null
+++ b/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/di/DatastoreCredentialsCacheFactory.java
@@ -0,0 +1,22 @@
+package org.opengroup.osdu.indexer.di;
+
+import org.opengroup.osdu.core.common.cache.ICache;
+import org.opengroup.osdu.core.common.cache.VmCache;
+import org.opengroup.osdu.core.gcp.multitenancy.credentials.DatastoreCredential;
+import org.springframework.beans.factory.config.AbstractFactoryBean;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DatastoreCredentialsCacheFactory extends
+    AbstractFactoryBean<ICache<String, DatastoreCredential>> {
+
+  @Override
+  public Class<?> getObjectType() {
+    return ICache.class;
+  }
+
+  @Override
+  protected ICache<String, DatastoreCredential> createInstance() throws Exception {
+    return new VmCache<>(5 * 60, 20);
+  }
+}
diff --git a/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/kms/KmsClient.java b/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/kms/KmsClient.java
deleted file mode 100644
index 020efb85e..000000000
--- a/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/kms/KmsClient.java
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2017-2019, Schlumberger
-//
-// 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.
-
-package org.opengroup.osdu.indexer.kms;
-
-import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
-import com.google.api.client.http.HttpTransport;
-import com.google.api.client.http.javanet.NetHttpTransport;
-import com.google.api.client.json.JsonFactory;
-import com.google.api.client.json.jackson2.JacksonFactory;
-import com.google.api.services.cloudkms.v1.CloudKMS;
-import com.google.api.services.cloudkms.v1.CloudKMSScopes;
-import com.google.api.services.cloudkms.v1.model.DecryptRequest;
-import com.google.api.services.cloudkms.v1.model.DecryptResponse;
-import com.google.api.services.cloudkms.v1.model.EncryptRequest;
-import com.google.api.services.cloudkms.v1.model.EncryptResponse;
-import org.opengroup.osdu.core.common.provider.interfaces.IKmsClient;
-import org.opengroup.osdu.core.common.search.Preconditions;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-import org.springframework.web.context.annotation.RequestScope;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-@Component
-@RequestScope
-public class KmsClient implements IKmsClient {
-
-    @Value("${GOOGLE_CLOUD_PROJECT}")
-    private String GOOGLE_CLOUD_PROJECT;
-
-    @Value("${KMS_KEY}")
-    private String KMS_KEY;
-
-    @Value("${KEY_RING}")
-    private String KEY_RING;
-
-    private static final String KEY_NAME = "projects/%s/locations/global/keyRings/%s/cryptoKeys/%s";
-
-    /**
-     * Encrypts the given plaintext using the specified crypto key.
-     * Google KMS automatically uses the new primary key version to encrypt data, so this could be directly used for key rotation
-     */
-    public String encryptString(String textToBeEncrypted) throws IOException {
-        Preconditions.checkNotNullOrEmpty(textToBeEncrypted, "textToBeEncrypted cannot be null");
-
-        byte[] plaintext = textToBeEncrypted.getBytes(StandardCharsets.UTF_8);
-        String resourceName = String.format(KEY_NAME, GOOGLE_CLOUD_PROJECT, KEY_RING, KMS_KEY);
-        CloudKMS kms = createAuthorizedClient();
-        EncryptRequest request = new EncryptRequest().encodePlaintext(plaintext);
-        EncryptResponse response = kms.projects().locations().keyRings().cryptoKeys()
-                .encrypt(resourceName, request)
-                .execute();
-        return response.getCiphertext();
-    }
-
-    /**
-     * Decrypts the provided ciphertext with the specified crypto key.
-     * Google KMS automatically uses the correct key version to decrypt data, as long as the key version is not disabled
-     */
-    public String decryptString(String textToBeDecrypted) throws IOException {
-        Preconditions.checkNotNullOrEmpty(textToBeDecrypted, "textToBeDecrypted cannot be null");
-
-        CloudKMS kms = createAuthorizedClient();
-        String cryptoKeyName = String.format(KEY_NAME, GOOGLE_CLOUD_PROJECT, KEY_RING, KMS_KEY);
-        DecryptRequest request = new DecryptRequest().setCiphertext(textToBeDecrypted);
-        DecryptResponse response = kms.projects().locations().keyRings().cryptoKeys()
-                .decrypt(cryptoKeyName, request)
-                .execute();
-        return new String(response.decodePlaintext(), StandardCharsets.UTF_8).trim();
-    }
-
-    /**
-     * Creates an authorized CloudKMS client service using Application Default Credentials.
-     *
-     * @return an authorized CloudKMS client
-     * @throws IOException if there's an error getting the default credentials.
-     */
-    private CloudKMS createAuthorizedClient() throws IOException {
-        HttpTransport transport = new NetHttpTransport();
-        JsonFactory jsonFactory = new JacksonFactory();
-        GoogleCredential credential = GoogleCredential.getApplicationDefault();
-        if (credential.createScopedRequired()) {
-            credential = credential.createScoped(CloudKMSScopes.all());
-        }
-        return new CloudKMS.Builder(transport, jsonFactory, credential)
-                .setApplicationName("CloudKMS snippets")
-                .build();
-    }
-}
\ No newline at end of file
diff --git a/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/DatastoreCredential.java b/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/DatastoreCredential.java
deleted file mode 100644
index 5bde4448c..000000000
--- a/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/DatastoreCredential.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2017-2019, Schlumberger
-//
-// 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.
-
-package org.opengroup.osdu.indexer.persistence;
-
-import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
-import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
-import com.google.api.client.json.JsonFactory;
-import com.google.api.client.json.jackson2.JacksonFactory;
-import com.google.api.services.iam.v1.Iam;
-import com.google.api.services.iam.v1.Iam.Projects.ServiceAccounts.SignJwt;
-import com.google.api.services.iam.v1.model.SignJwtRequest;
-import com.google.api.services.iam.v1.model.SignJwtResponse;
-import com.google.auth.oauth2.AccessToken;
-import com.google.auth.oauth2.GoogleCredentials;
-import com.google.gson.JsonObject;
-import org.apache.commons.lang3.time.DateUtils;
-import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
-import org.opengroup.osdu.core.common.util.Crc32c;
-import org.opengroup.osdu.indexer.cache.DatastoreCredentialCache;
-
-import java.util.Date;
-
-public class DatastoreCredential extends GoogleCredentials {
-
-	private static final long serialVersionUID = 8344377091688956815L;
-	private static final JsonFactory JSON_FACTORY = new JacksonFactory();
-	private Iam iam;
-
-	private final TenantInfo tenant;
-	private final DatastoreCredentialCache cache;
-
-	protected DatastoreCredential(TenantInfo tenant, DatastoreCredentialCache cache) {
-		this.tenant = tenant;
-		this.cache = cache;
-	}
-
-	@Override
-	public AccessToken refreshAccessToken() {
-
-		String cacheKey = this.getCacheKey();
-
-		AccessToken accessToken = this.cache.get(cacheKey);
-
-		if (accessToken != null) {
-			return accessToken;
-		}
-
-		try {
-			SignJwtRequest signJwtRequest = new SignJwtRequest();
-			signJwtRequest.setPayload(this.getPayload());
-
-			String serviceAccountName = String.format("projects/-/serviceAccounts/%s", this.tenant.getServiceAccount());
-
-			SignJwt signJwt = this.getIam().projects().serviceAccounts().signJwt(serviceAccountName, signJwtRequest);
-
-			SignJwtResponse signJwtResponse = signJwt.execute();
-			String signedJwt = signJwtResponse.getSignedJwt();
-
-			accessToken = new AccessToken(signedJwt, DateUtils.addSeconds(new Date(), 3600));
-
-			this.cache.put(cacheKey, accessToken);
-
-			return accessToken;
-		} catch (Exception e) {
-			throw new RuntimeException("Error creating datastore credential", e);
-		}
-	}
-
-	private String getPayload() {
-		JsonObject payload = new JsonObject();
-		payload.addProperty("iss", this.tenant.getServiceAccount());
-		payload.addProperty("sub", this.tenant.getServiceAccount());
-		payload.addProperty("aud", "https://datastore.googleapis.com/google.datastore.v1.Datastore");
-		payload.addProperty("iat", System.currentTimeMillis() / 1000);
-
-		return payload.toString();
-	}
-
-	protected void setIam(Iam iam) {
-		this.iam = iam;
-	}
-
-	private Iam getIam() throws Exception {
-		if (this.iam == null) {
-
-			Iam.Builder builder = new Iam.Builder(GoogleNetHttpTransport.newTrustedTransport(), JSON_FACTORY,
-					GoogleCredential.getApplicationDefault()).setApplicationName("Search Service");
-
-			this.iam = builder.build();
-		}
-		return this.iam;
-	}
-
-	private String getCacheKey() {
-		return Crc32c.hashToBase64EncodedString(String.format("datastoreCredential:%s", this.tenant.getName()));
-	}
-}
\ No newline at end of file
diff --git a/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/DatastoreFactory.java b/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/DatastoreFactory.java
deleted file mode 100644
index 1a1487015..000000000
--- a/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/DatastoreFactory.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017-2019, Schlumberger
-//
-// 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.
-
-package org.opengroup.osdu.indexer.persistence;
-
-import com.google.api.gax.retrying.RetrySettings;
-import com.google.cloud.TransportOptions;
-import com.google.cloud.datastore.Datastore;
-import com.google.cloud.datastore.DatastoreOptions;
-import com.google.cloud.http.HttpTransportOptions;
-import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
-import org.opengroup.osdu.indexer.cache.DatastoreCredentialCache;
-import org.springframework.stereotype.Component;
-import org.threeten.bp.Duration;
-
-import javax.inject.Inject;
-import java.util.HashMap;
-import java.util.Map;
-
-@Component
-public class DatastoreFactory {
-
-    @Inject
-    private DatastoreCredentialCache cache;
-
-    private static Map<String, Datastore> DATASTORE_CLIENTS = new HashMap<>();
-
-    private static final RetrySettings RETRY_SETTINGS = RetrySettings.newBuilder()
-            .setMaxAttempts(6)
-            .setInitialRetryDelay(Duration.ofSeconds(10))
-            .setMaxRetryDelay(Duration.ofSeconds(32))
-            .setRetryDelayMultiplier(2.0)
-            .setTotalTimeout(Duration.ofSeconds(50))
-            .setInitialRpcTimeout(Duration.ofSeconds(50))
-            .setRpcTimeoutMultiplier(1.0)
-            .setMaxRpcTimeout(Duration.ofSeconds(50))
-            .build();
-
-    private static final TransportOptions TRANSPORT_OPTIONS = HttpTransportOptions.newBuilder()
-            .setReadTimeout(30000)
-            .build();
-
-    public Datastore getDatastoreInstance(TenantInfo tenantInfo) {
-        if (DATASTORE_CLIENTS.get(tenantInfo.getName()) == null) {
-            Datastore googleDatastore = DatastoreOptions.newBuilder()
-                    .setCredentials(new DatastoreCredential(tenantInfo, this.cache))
-                    .setRetrySettings(RETRY_SETTINGS)
-                    .setTransportOptions(TRANSPORT_OPTIONS)
-                    .setNamespace(tenantInfo.getName())
-                    .setProjectId(tenantInfo.getProjectId())
-                    .build().getService();
-            DATASTORE_CLIENTS.put(tenantInfo.getName(), googleDatastore);
-        }
-        return DATASTORE_CLIENTS.get(tenantInfo.getName());
-    }
-}
diff --git a/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/ElasticRepositoryDatastore.java b/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/ElasticRepositoryDatastore.java
index 9c0b6b6ee..3562fff40 100644
--- a/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/ElasticRepositoryDatastore.java
+++ b/provider/indexer-gcp/src/main/java/org/opengroup/osdu/indexer/persistence/ElasticRepositoryDatastore.java
@@ -26,6 +26,7 @@ import org.opengroup.osdu.core.common.model.http.AppException;
 import org.opengroup.osdu.core.common.provider.interfaces.IKmsClient;
 import org.opengroup.osdu.core.common.provider.interfaces.IElasticRepository;
 import org.opengroup.osdu.core.common.search.Preconditions;
+import org.opengroup.osdu.core.gcp.multitenancy.DatastoreFactory;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 import javax.inject.Inject;
@@ -52,7 +53,7 @@ public class ElasticRepositoryDatastore implements IElasticRepository {
     @Override
     public ClusterSettings getElasticClusterSettings(TenantInfo tenantInfo) {
 
-        Datastore googleDatastore = this.datastoreFactory.getDatastoreInstance(tenantInfo);
+        Datastore googleDatastore = this.datastoreFactory.getDatastore(tenantInfo);
         Key key = googleDatastore.newKeyFactory().setKind(ELASTIC_DATASTORE_KIND).newKey(ELASTIC_DATASTORE_ID);
         Entity datastoreEntity = googleDatastore.get(key);
 
diff --git a/provider/indexer-gcp/src/main/resources/application-kuber.properties b/provider/indexer-gcp/src/main/resources/application-kuber.properties
index af85274d4..bc2a5f901 100644
--- a/provider/indexer-gcp/src/main/resources/application-kuber.properties
+++ b/provider/indexer-gcp/src/main/resources/application-kuber.properties
@@ -21,4 +21,4 @@ REDIS_SEARCH_HOST=${REDIS_SEARCH_HOST}
 GOOGLE_AUDIENCES=${GOOGLE_AUDIENCES}
 
 DEPLOYMENT_ENVIRONMENT=CLOUD
-disable.appengine.log.factory=true
+disable.appengine.log.factory=true
\ No newline at end of file
diff --git a/provider/indexer-gcp/src/main/resources/application-testing.properties b/provider/indexer-gcp/src/main/resources/application-testing.properties
index 5368d291c..fb0c9e2d6 100644
--- a/provider/indexer-gcp/src/main/resources/application-testing.properties
+++ b/provider/indexer-gcp/src/main/resources/application-testing.properties
@@ -18,4 +18,4 @@ CRS_API=https://crs-converter-gae-dot-opendes-evt.appspot.com/api/crs/v1
 REDIS_GROUP_HOST=10.253.209.196
 REDIS_SEARCH_HOST=10.118.2.140
 
-GOOGLE_AUDIENCES=833591776864-oobhqvmtdg9rpreubjvn44m5f8revglk.apps.googleusercontent.com
+GOOGLE_AUDIENCES=833591776864-oobhqvmtdg9rpreubjvn44m5f8revglk.apps.googleusercontent.com
\ No newline at end of file
diff --git a/provider/indexer-gcp/src/main/resources/application.properties b/provider/indexer-gcp/src/main/resources/application.properties
index 1ccde0848..a0cbce342 100644
--- a/provider/indexer-gcp/src/main/resources/application.properties
+++ b/provider/indexer-gcp/src/main/resources/application.properties
@@ -32,3 +32,5 @@ KMS_KEY=searchService
 
 ELASTIC_DATASTORE_KIND=SearchSettings
 ELASTIC_DATASTORE_ID=indexer-service
+
+security.https.certificate.trust=false
diff --git a/provider/indexer-ibm/pom.xml b/provider/indexer-ibm/pom.xml
index ae6917c09..07b269a72 100644
--- a/provider/indexer-ibm/pom.xml
+++ b/provider/indexer-ibm/pom.xml
@@ -33,7 +33,7 @@
     <packaging>jar</packaging>
 	
 	<properties>
-		<os-core-lib-ibm.version>0.0.18</os-core-lib-ibm.version>
+		<os-core-lib-ibm.version>0.3.8-SNAPSHOT</os-core-lib-ibm.version>
 	</properties>
 	
 	<profiles>
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/SecurityConfig.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/SecurityConfig.java
index 2cf274bea..561edbc1e 100644
--- a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/SecurityConfig.java
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/security/SecurityConfig.java
@@ -29,6 +29,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
                 .csrf().disable()
                 .authorizeRequests()
                 .antMatchers("/", "/index.html",
+                		"/liveness_check",
+                		"/readiness_check",
                         "/index-worker", "/_dps/task-handlers", "/_dps/task-handlers/**",
                         "/reindex",
                         "/v2/api-docs",
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/GlobalExceptionMapper.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/GlobalExceptionMapper.java
new file mode 100644
index 000000000..60e0be562
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/GlobalExceptionMapper.java
@@ -0,0 +1,83 @@
+// Copyright 2017-2019, Schlumberger
+//
+// 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.
+
+package org.opengroup.osdu.indexer.ibm.util;
+
+import javax.validation.ValidationException;
+
+import javassist.NotFoundException;
+import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+import org.opengroup.osdu.core.common.model.http.AppException;
+
+@Order(Ordered.HIGHEST_PRECEDENCE)
+@ControllerAdvice
+public class GlobalExceptionMapper extends ResponseEntityExceptionHandler {
+
+    @Autowired
+    private JaxRsDpsLog logger;
+
+    @ExceptionHandler(AppException.class)
+    protected ResponseEntity<Object> handleAppException(AppException e) {
+        return this.getErrorResponse(e);
+    }
+
+    @ExceptionHandler(ValidationException.class)
+    protected ResponseEntity<Object> handleValidationException(ValidationException e) {
+        return this.getErrorResponse(
+                new AppException(HttpStatus.BAD_REQUEST.value(), "Validation error.", e.getMessage(), e));
+    }
+
+    @ExceptionHandler(NotFoundException.class)
+    protected ResponseEntity<Object> handleNotFoundException(NotFoundException e) {
+        return this.getErrorResponse(
+                new AppException(HttpStatus.NOT_FOUND.value(), "Resource not found.", e.getMessage(), e));
+    }
+
+    @ExceptionHandler(AccessDeniedException.class)
+    protected ResponseEntity<Object> handleAccessDeniedException(AccessDeniedException e) {
+        return this.getErrorResponse(
+                new AppException(HttpStatus.FORBIDDEN.value(), "Access denied", e.getMessage(), e));
+    }
+
+    @ExceptionHandler(Exception.class)
+    protected ResponseEntity<Object> handleGeneralException(Exception e) {
+        return this.getErrorResponse(
+                new AppException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Server error.",
+                        "An unknown error has occurred.", e));
+    }
+
+    private ResponseEntity<Object> getErrorResponse(AppException e) {
+
+        String exceptionMsg = e.getOriginalException() != null
+                ? e.getOriginalException().getMessage()
+                : e.getError().getMessage();
+
+        if (e.getError().getCode() > 499) {
+            this.logger.error(exceptionMsg, e);
+        } else {
+            this.logger.warning(exceptionMsg, e);
+        }
+
+        return new ResponseEntity<Object>(e.getError(), HttpStatus.resolve(e.getError().getCode()));
+    }
+}
\ No newline at end of file
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/IndexerQueueTaskBuilderIbm.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/IndexerQueueTaskBuilderIbm.java
new file mode 100644
index 000000000..a429d1455
--- /dev/null
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/IndexerQueueTaskBuilderIbm.java
@@ -0,0 +1,86 @@
+package org.opengroup.osdu.indexer.ibm.util;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.core.common.model.search.RecordChangedMessages;
+import org.opengroup.osdu.core.ibm.messagebus.IMessageFactory;
+import org.opengroup.osdu.indexer.util.IndexerQueueTaskBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+
+@Primary
+@Component
+public class IndexerQueueTaskBuilderIbm extends IndexerQueueTaskBuilder {
+	private static final Logger logger = LoggerFactory.getLogger(IndexerQueueTaskBuilderIbm.class);
+	
+	@Inject
+	IMessageFactory mq;
+	
+	private Gson gson;
+	private final static String RETRY_STRING = "retry";
+	private final static String ERROR_CODE = "errorCode";
+	private final static String ERROR_MESSAGE = "errorMessage";
+
+	@Inject
+	public void init() {
+		gson = new Gson();
+	}
+
+	@Override
+	public void createWorkerTask(String payload, DpsHeaders headers) {
+		createTask(payload, headers);
+	}
+
+	@Override
+	public void createReIndexTask(String payload, DpsHeaders headers) {
+		createTask(payload, headers);
+	}
+
+	private void createTask(String payload, DpsHeaders headers) {
+
+		try {
+			RecordChangedMessages receivedPayload = gson.fromJson(payload, RecordChangedMessages.class);
+
+			Map<String, String> attributes = receivedPayload.getAttributes();
+			int retryCount = 0;
+			if (attributes.containsKey(RETRY_STRING)) {
+				retryCount = Integer.parseInt(attributes.get(RETRY_STRING));
+				retryCount++;
+			} else {
+				retryCount = 1;
+			}
+			attributes.put(RETRY_STRING, String.valueOf(retryCount));
+			attributes.put(ERROR_CODE, "999"); //error code TBD 
+			attributes.put(ERROR_MESSAGE, "Indexer could not process record");
+			receivedPayload.setAttributes(attributes);
+
+			// incase if we need to shift logic from indexer-queue-ibm/subscriber.java
+			/*
+			 * if(Integer.parseInt(receivedPayload.getAttributes().get(RETRY_STRING))>3) {
+			 * //add DLQ in IMessageFactory
+			 * 
+			 * mq.sendMessage("DLQ", gson.toJson(receivedPayload)); }
+			 */
+			logger.info("Message send back to queue : " + receivedPayload);
+			mq.sendMessage(IMessageFactory.DEFAULT_QUEUE_NAME, gson.toJson(receivedPayload));
+		} catch (JsonSyntaxException e) {
+			logger.error("JsonSyntaxException in IndexerQueueTaskBuilderIbm " + e.toString());
+			e.printStackTrace();
+		} catch (NumberFormatException e) {
+			logger.error("NumberFormatException in IndexerQueueTaskBuilderIbm " + e.toString());
+			e.printStackTrace();
+		} catch (Exception e) {
+			logger.error("Exception in IndexerQueueTaskBuilderIbm " + e.toString());
+			e.printStackTrace();
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/RequestInfoImpl.java b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/RequestInfoImpl.java
index 12fd218f8..6ddf941be 100644
--- a/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/RequestInfoImpl.java
+++ b/provider/indexer-ibm/src/main/java/org/opengroup/osdu/indexer/ibm/util/RequestInfoImpl.java
@@ -53,10 +53,14 @@ public class RequestInfoImpl implements IRequestInfo {
 
     @Inject
     private TenantInfo tenantInfo;
-
+    
     @Value("${DEPLOYMENT_ENVIRONMENT}")
     private String DEPLOYMENT_ENVIRONMENT;
 
+    private static final String INDEXER_API_KEY_HEADER="x-api-key";
+    
+    @Value("${INDEXER_API_KEY}")
+    private String tokenFromProperty;
 
     @Override
     public DpsHeaders getHeaders() {
@@ -65,9 +69,19 @@ public class RequestInfoImpl implements IRequestInfo {
             // throw to prevent null reference exception below
             throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Invalid Headers", "Headers Map DpsHeaders is null");
         }
-        DpsHeaders headers = this.getCoreServiceHeaders(headersMap.getHeaders());
-        return headers;
-    }
+		DpsHeaders headers = this.getCoreServiceHeaders(headersMap.getHeaders());
+
+		if (headers.getHeaders().containsKey(INDEXER_API_KEY_HEADER)) {
+			String apiToken = headers.getHeaders().get(INDEXER_API_KEY_HEADER);
+			if (!apiToken.equals(tokenFromProperty)) {
+				logger.error("Indexer API Token in header is mismatched");
+				throw new AppException(HttpStatus.SC_UNAUTHORIZED, "Indexer API Token in header mismatched.", "Indexer API Token in header mismatched.");
+			}
+		} else {
+			throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Missing Header", "The headers "+ INDEXER_API_KEY_HEADER + "  is missing!");
+		}
+		return headers;
+	}
 
     @Override
     public String getPartitionId() {
@@ -95,7 +109,8 @@ public class RequestInfoImpl implements IRequestInfo {
 
     @Override
     public boolean isTaskQueueRequest() {
-        //if (!this.dpsHeaders.getHeaders().containsKey(INDEXER_QUEUE_KEY)) return false;
+        //if (!this.dpsHeaders.getHeaders().containsKey(INDEXER_API_KEY_HEADER)) return false;
+    	
 
 //        String queueId = this.headersInfo.getHeadersMap().get(AppEngineHeaders.TASK_QUEUE_NAME);
 //        return queueId.endsWith(Constants.INDEXER_QUEUE_IDENTIFIER);
diff --git a/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/HTTPClient.java b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/HTTPClient.java
index 28d7aa69c..1ce98c78a 100644
--- a/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/HTTPClient.java
+++ b/testing/indexer-test-core/src/main/java/org/opengroup/osdu/util/HTTPClient.java
@@ -62,6 +62,7 @@ public abstract class HTTPClient {
             Client client = getClient();
             client.setReadTimeout(180000);
             client.setConnectTimeout(10000);
+            log.info("URL: = " + url);
             WebResource webResource = client.resource(url);
             response = this.getClientResponse(httpMethod, payLoad, webResource, headers, token);
         } catch (Exception e) {
diff --git a/testing/indexer-test-core/src/main/resources/features/indexrecord/IndexRecord.feature b/testing/indexer-test-core/src/main/resources/features/indexrecord/IndexRecord.feature
index 30ee32eaa..c281ec4a0 100644
--- a/testing/indexer-test-core/src/main/resources/features/indexrecord/IndexRecord.feature
+++ b/testing/indexer-test-core/src/main/resources/features/indexrecord/IndexRecord.feature
@@ -15,8 +15,8 @@ Feature: Indexing of the documents
 
     Examples:
       | kind                                      | recordFile        | number | index                                     | type   | acl                                    | mapping                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
-      | "tenant1:testindex<timestamp>:well:1.0.0" | "index_records_1" | 5      | "tenant1-testindex<timestamp>-well-1.0.0" | "well" | "data.default.viewers@opendes" | "{"mappings":{"well":{"dynamic":"false","properties":{"acl":{"properties":{"owners":{"type":"keyword"},"viewers":{"type":"keyword"}}},"ancestry":{"properties":{"parents":{"type":"keyword"}}},"data":{"properties":{"Basin":{"type":"text"},"Country":{"type":"text"},"County":{"type":"text"},"EmptyAttribute":{"type":"text"},"Established":{"type":"date"},"Field":{"type":"text"},"Location":{"type":"geo_point"},"OriginalOperator":{"type":"text"},"Rank":{"type":"integer"},"Score":{"type":"integer"},"State":{"type":"text"},"WellName":{"type":"text"},"WellStatus":{"type":"text"},"WellType":{"type":"text"},"DblArray":{"type":"double"}}},"id":{"type":"keyword"},"index":{"properties":{"lastUpdateTime":{"type":"date"},"statusCode":{"type":"integer"},"trace":{"type":"text"}}},"kind":{"type":"keyword"},"legal":{"properties":{"legaltags":{"type":"keyword"},"otherRelevantDataCountries":{"type":"keyword"},"status":{"type":"keyword"}}},"namespace":{"type":"keyword"},"type":{"type":"keyword"},"version":{"type":"long"},"x-acl":{"type":"keyword"}}}}}" |
-      | "tenant1:testindex<timestamp>:well:3.0.0" | "index_records_1" | 5      | "tenant1-testindex<timestamp>-well-3.0.0" | "well" | "data.default.viewers@opendes" | "{"mappings":{"well":{"dynamic":"false","properties":{"acl":{"properties":{"owners":{"type":"keyword"},"viewers":{"type":"keyword"}}},"ancestry":{"properties":{"parents":{"type":"keyword"}}},"data":{"properties":{"Basin":{"type":"text"},"Country":{"type":"text"},"County":{"type":"text"},"EmptyAttribute":{"type":"text"},"Established":{"type":"date"},"Field":{"type":"text"},"Location":{"type":"geo_point"},"OriginalOperator":{"type":"text"},"Rank":{"type":"integer"},"Score":{"type":"integer"},"State":{"type":"text"},"WellName":{"type":"text"},"WellStatus":{"type":"text"},"WellType":{"type":"text"},"DblArray":{"type":"double"}}},"id":{"type":"keyword"},"index":{"properties":{"lastUpdateTime":{"type":"date"},"statusCode":{"type":"integer"},"trace":{"type":"text"}}},"kind":{"type":"keyword"},"legal":{"properties":{"legaltags":{"type":"keyword"},"otherRelevantDataCountries":{"type":"keyword"},"status":{"type":"keyword"}}},"namespace":{"type":"keyword"},"type":{"type":"keyword"},"version":{"type":"long"},"x-acl":{"type":"keyword"}}}}}" |
+      | "tenant1:testindex<timestamp>:well:1.0.0" | "index_records_1" | 5      | "tenant1-testindex<timestamp>-well-1.0.0" | "well" | "data.default.viewers@tenant1" | "{"mappings":{"well":{"dynamic":"false","properties":{"acl":{"properties":{"owners":{"type":"keyword"},"viewers":{"type":"keyword"}}},"ancestry":{"properties":{"parents":{"type":"keyword"}}},"data":{"properties":{"Basin":{"type":"text"},"Country":{"type":"text"},"County":{"type":"text"},"EmptyAttribute":{"type":"text"},"Established":{"type":"date"},"Field":{"type":"text"},"Location":{"type":"geo_point"},"OriginalOperator":{"type":"text"},"Rank":{"type":"integer"},"Score":{"type":"integer"},"State":{"type":"text"},"WellName":{"type":"text"},"WellStatus":{"type":"text"},"WellType":{"type":"text"},"DblArray":{"type":"double"}}},"id":{"type":"keyword"},"index":{"properties":{"lastUpdateTime":{"type":"date"},"statusCode":{"type":"integer"},"trace":{"type":"text"}}},"kind":{"type":"keyword"},"legal":{"properties":{"legaltags":{"type":"keyword"},"otherRelevantDataCountries":{"type":"keyword"},"status":{"type":"keyword"}}},"namespace":{"type":"keyword"},"type":{"type":"keyword"},"version":{"type":"long"},"x-acl":{"type":"keyword"}}}}}" |
+      | "tenant1:testindex<timestamp>:well:3.0.0" | "index_records_1" | 5      | "tenant1-testindex<timestamp>-well-3.0.0" | "well" | "data.default.viewers@tenant1" | "{"mappings":{"well":{"dynamic":"false","properties":{"acl":{"properties":{"owners":{"type":"keyword"},"viewers":{"type":"keyword"}}},"ancestry":{"properties":{"parents":{"type":"keyword"}}},"data":{"properties":{"Basin":{"type":"text"},"Country":{"type":"text"},"County":{"type":"text"},"EmptyAttribute":{"type":"text"},"Established":{"type":"date"},"Field":{"type":"text"},"Location":{"type":"geo_point"},"OriginalOperator":{"type":"text"},"Rank":{"type":"integer"},"Score":{"type":"integer"},"State":{"type":"text"},"WellName":{"type":"text"},"WellStatus":{"type":"text"},"WellType":{"type":"text"},"DblArray":{"type":"double"}}},"id":{"type":"keyword"},"index":{"properties":{"lastUpdateTime":{"type":"date"},"statusCode":{"type":"integer"},"trace":{"type":"text"}}},"kind":{"type":"keyword"},"legal":{"properties":{"legaltags":{"type":"keyword"},"otherRelevantDataCountries":{"type":"keyword"},"status":{"type":"keyword"}}},"namespace":{"type":"keyword"},"type":{"type":"keyword"},"version":{"type":"long"},"x-acl":{"type":"keyword"}}}}}" |
 
   Scenario Outline: Ingest the record and Index in the Elastic Search with bad attribute
     When I ingest records with the <recordFile> with <acl> for a given <kind>
@@ -24,5 +24,5 @@ Feature: Indexing of the documents
 
     Examples:
       | kind                                      | recordFile        | number | index                                     | skippedAttribute | acl                            |
-      | "tenant1:testindex<timestamp>:well:2.0.0" | "index_records_2" | 4      | "tenant1-testindex<timestamp>-well-2.0.0" | "data.Location"  | "data.default.viewers@opendes" |
-      | "tenant1:testindex<timestamp>:well:3.0.0" | "index_records_3" | 7      | "tenant1-testindex<timestamp>-well-3.0.0" | "data.GeoShape"  | "data.default.viewers@opendes" |
+      | "tenant1:testindex<timestamp>:well:2.0.0" | "index_records_2" | 4      | "tenant1-testindex<timestamp>-well-2.0.0" | "data.Location"  | "data.default.viewers@tenant1" |
+      | "tenant1:testindex<timestamp>:well:3.0.0" | "index_records_3" | 7      | "tenant1-testindex<timestamp>-well-3.0.0" | "data.GeoShape"  | "data.default.viewers@tenant1" |
diff --git a/testing/indexer-test-ibm/pom.xml b/testing/indexer-test-ibm/pom.xml
index c9119b69b..62842b147 100644
--- a/testing/indexer-test-ibm/pom.xml
+++ b/testing/indexer-test-ibm/pom.xml
@@ -14,7 +14,7 @@
         <maven.compiler.target>1.8</maven.compiler.target>
         <maven.compiler.source>1.8</maven.compiler.source>
         <cucumber.version>1.2.5</cucumber.version>
-        <os-core-lib-ibm.version>0.0.18</os-core-lib-ibm.version>
+        <os-core-lib-ibm.version>0.3.8-SNAPSHOT</os-core-lib-ibm.version>
     </properties>
 
     <dependencies>
diff --git a/testing/indexer-test-ibm/src/main/resources/apikey.feature b/testing/indexer-test-ibm/src/main/resources/apikey.feature
new file mode 100644
index 000000000..4be8e4eb3
--- /dev/null
+++ b/testing/indexer-test-ibm/src/main/resources/apikey.feature
@@ -0,0 +1,10 @@
+Feature: check the api key in header
+  This feature check the api key received from headers is matching with configured in environment
+
+  Scenario Outline: compare the api key with configured environment key
+    When I pass api key
+    Then compare with key configured in properties file
+
+    Examples:
+      | apiKey    | 
+      | "abcd" 	  | 
diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/RunTest.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/RunTest.java
index 4978ddfcc..897f02c15 100644
--- a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/RunTest.java
+++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/RunTest.java
@@ -6,7 +6,7 @@ import org.junit.runner.RunWith;
 
 @RunWith(Cucumber.class)
 @CucumberOptions(
-        features = "classpath:features/indexrecord/IndexRecord.feature",
+        features = {"classpath:features/indexrecord/IndexRecord.feature","classpath:apikey.feature"},
         glue = {"classpath:org.opengroup.osdu.step_definitions/index/record"},
         plugin = {"pretty", "junit:target/cucumber-reports/TEST-indexrecord.xml"})
 public class RunTest {
diff --git a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java
index 518b2b6f9..3c885b11c 100644
--- a/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java
+++ b/testing/indexer-test-ibm/src/test/java/org/opengroup/osdu/step_definitions/index/record/Steps.java
@@ -1,8 +1,25 @@
 package org.opengroup.osdu.step_definitions.index.record;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
 import org.opengroup.osdu.common.RecordSteps;
+import org.opengroup.osdu.core.common.Constants;
+import org.opengroup.osdu.core.common.http.HttpClient;
+import org.opengroup.osdu.core.common.http.HttpRequest;
+import org.opengroup.osdu.core.common.http.HttpResponse;
+import org.opengroup.osdu.core.common.model.http.AppError;
+import org.opengroup.osdu.core.common.model.http.DpsHeaders;
+import org.opengroup.osdu.core.common.model.search.RecordChangedMessages;
+import org.opengroup.osdu.core.ibm.util.Config;
 import org.opengroup.osdu.util.IBMHTTPClient;
 
+import com.google.gson.Gson;
+
 import cucumber.api.DataTable;
 import cucumber.api.Scenario;
 import cucumber.api.java.Before;
@@ -48,5 +65,46 @@ public class Steps extends RecordSteps {
     public void iShouldGetTheNumberDocumentsForTheIndexInTheElasticSearchWithOutSkippedAttribute(int expectedCount, String index, String skippedAttributes) throws Throwable {
         super.iShouldGetTheNumberDocumentsForTheIndexInTheElasticSearchWithOutSkippedAttribute(expectedCount, index, skippedAttributes);
     }
-
+    
+    @When("^I pass api key$")
+    public void i_pass_the_api_key() {
+    }
+    
+    @Then("^compare with key configured in properties file$")
+    public void compare_with_key_configured_in_propertiesFile() throws Throwable {
+    	
+    	final String CORRELATION_ID = "1234";
+        final String OPENDES = "opendes";
+    	final Gson gson = new Gson();
+    	
+    	String INDEXER_API_KEY = Config.getEnvironmentVariable("INDEXER_API_KEY");
+    	String INDEXER_HOST_URL = Config.getEnvironmentVariable("INDEXER_HOST_URL");
+    	
+    	RecordChangedMessages recordChangeMessage = new RecordChangedMessages();
+    	
+    	String data = "[{\"id\":\"opendes:doc:1234\",\"kind\":\"opendes:test:test:1.0.0\",\"op\":\"create\"}]";
+    	
+    	Map<String, String> attributes = new HashMap<>();
+    	attributes.put("correlation-id", CORRELATION_ID);
+    	attributes.put("data-partition-id", OPENDES);
+    	recordChangeMessage.setAttributes(attributes);
+    	recordChangeMessage.setData(data);
+    	
+    	String url = StringUtils.join(INDEXER_HOST_URL, Constants.WORKER_RELATIVE_URL);
+		HttpClient httpClient = new HttpClient();
+		DpsHeaders dpsHeaders = new DpsHeaders();
+		dpsHeaders.put("x-api-key", INDEXER_API_KEY);
+		dpsHeaders.put("correlation-id", CORRELATION_ID);
+		dpsHeaders.put("data-partition-id", OPENDES);
+		
+		HttpRequest rq = HttpRequest.post(recordChangeMessage).url(url).headers(dpsHeaders.getHeaders()).build();
+		HttpResponse result = httpClient.send(rq);
+		if(result.hasException().equals(false) && result.getResponseCode() == 500) {
+			assertTrue(result.getResponseCode() == 500);
+		} else {
+			AppError error = gson.fromJson(result.getBody(), AppError.class);
+			assertFalse("Token is mismatched", error.getCode() == 401);
+		}
+		
+    }
 }
\ No newline at end of file
-- 
GitLab