Repository: companieshouse/test-data-generator
Pull request #140: Feature/tdg elastic search
Author: login: lyadavalli
Created: 2025-04-22T10:20:35Z | Merged: 2025-04-25T13:04:06Z



Diff:
diff --git a/pom.xml b/pom.xml
index 8af2980a1..eb8abe3b5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,6 +36,8 @@
 
         <!-- internal dependencies -->
         <api-security-java.version>2.0.8</api-security-java.version>
+        <private-api-sdk-java.version>4.0.255</private-api-sdk-java.version>
+        <api-sdk-manager-java-library.version>3.0.6</api-sdk-manager-java-library.version>
     </properties>
 
     <dependencyManagement>
@@ -125,6 +127,16 @@
             <artifactId>commons-lang3</artifactId>
             <version>3.12.0</version>
         </dependency>
+        <dependency>
+            <groupId>uk.gov.companieshouse</groupId>
+            <artifactId>private-api-sdk-java</artifactId>
+            <version>${private-api-sdk-java.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>uk.gov.companieshouse</groupId>
+            <artifactId>api-sdk-manager-java-library</artifactId>
+            <version>${api-sdk-manager-java-library.version}</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/src/main/java/uk/gov/companieshouse/api/testdata/config/AppConfig.java b/src/main/java/uk/gov/companieshouse/api/testdata/config/AppConfig.java
index b358bd1f4..6bfb0e082 100644
--- a/src/main/java/uk/gov/companieshouse/api/testdata/config/AppConfig.java
+++ b/src/main/java/uk/gov/companieshouse/api/testdata/config/AppConfig.java
@@ -1,24 +1,50 @@
 package uk.gov.companieshouse.api.testdata.config;
 
+import java.time.Clock;
+import java.util.function.Supplier;
+
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-
-import java.time.Clock;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.http.ApiKeyHttpClient;
 
 /**
  * Main application configuration class.
  */
 @Configuration
-public class AppConfig {
+public class AppConfig implements WebMvcConfigurer {
     /**
      * Obtains a clock that returns the current instant, converting to date and time using the
      * UTC time-zone. Singleton bean provides consistent UTC timestamps.
      *
      * @return a Clock that uses the best available system clock in the UTC zone, not null
      */
+    private final String chsApiKey;
+    private final String apiUrl;
+
+    public AppConfig(@Value("${api-key}") String chsApiKey, @Value("${api-url}") String apiUrl) {
+        this.chsApiKey = chsApiKey;
+        this.apiUrl = apiUrl;
+    }
+
     @Bean
     public Clock clock() {
         return Clock.systemUTC();
     }
 
+    @Bean
+    public Supplier<InternalApiClient> internalApiClientSupplier() {
+        return () -> {
+            var apiKeyHttpClient = new ApiKeyHttpClient(chsApiKey);
+            apiKeyHttpClient.setRequestId(DataMapHolder.getRequestId());
+
+            var internalApiClient = new InternalApiClient(apiKeyHttpClient);
+            internalApiClient.setBasePath(apiUrl);
+
+            return internalApiClient;
+        };
+    }
+
 }
\ No newline at end of file
diff --git a/src/main/java/uk/gov/companieshouse/api/testdata/config/DataMapHolder.java b/src/main/java/uk/gov/companieshouse/api/testdata/config/DataMapHolder.java
new file mode 100644
index 000000000..6fa12b944
--- /dev/null
+++ b/src/main/java/uk/gov/companieshouse/api/testdata/config/DataMapHolder.java
@@ -0,0 +1,37 @@
+package uk.gov.companieshouse.api.testdata.config;
+
+import java.util.Map;
+import uk.gov.companieshouse.logging.util.DataMap.Builder;
+
+public class DataMapHolder {
+    private static final ThreadLocal<Builder> DATAMAP_BUILDER
+            = ThreadLocal.withInitial(() -> new Builder().requestId("uninitialised"));
+
+    public static void initialise(String requestId) {
+        DATAMAP_BUILDER.get().requestId(requestId);
+    }
+
+    private DataMapHolder() {}
+
+    public static void clear() {
+        DATAMAP_BUILDER.remove();
+    }
+
+    public static Builder get() {
+        return DATAMAP_BUILDER.get();
+    }
+
+    /**
+     * Get the Log Map from the Builder.
+     * @return Map
+     */
+    public static Map<String, Object> getLogMap() {
+        return DATAMAP_BUILDER.get()
+                .build()
+                .getLogMap();
+    }
+
+    public static String getRequestId() {
+        return (String) getLogMap().get("request_id");
+    }
+}
diff --git a/src/main/java/uk/gov/companieshouse/api/testdata/service/CompanySearchService.java b/src/main/java/uk/gov/companieshouse/api/testdata/service/CompanySearchService.java
new file mode 100644
index 000000000..d0733100e
--- /dev/null
+++ b/src/main/java/uk/gov/companieshouse/api/testdata/service/CompanySearchService.java
@@ -0,0 +1,13 @@
+package uk.gov.companieshouse.api.testdata.service;
+
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.exception.URIValidationException;
+import uk.gov.companieshouse.api.testdata.exception.DataException;
+import uk.gov.companieshouse.api.testdata.model.rest.CompanyData;
+
+public interface CompanySearchService {
+
+    void addCompanyIntoElasticSearchIndex(CompanyData data) throws DataException, ApiErrorResponseException, URIValidationException;
+
+    void deleteCompanyFromElasticSearchIndex(String companyNumber) throws DataException, ApiErrorResponseException, URIValidationException;
+}
diff --git a/src/main/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImpl.java b/src/main/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImpl.java
new file mode 100644
index 000000000..cbf5db39b
--- /dev/null
+++ b/src/main/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImpl.java
@@ -0,0 +1,68 @@
+package uk.gov.companieshouse.api.testdata.service.impl;
+
+import java.util.function.Supplier;
+import org.springframework.stereotype.Service;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.exception.URIValidationException;
+
+import uk.gov.companieshouse.api.testdata.exception.DataException;
+import uk.gov.companieshouse.api.testdata.model.rest.CompanyData;
+import uk.gov.companieshouse.api.testdata.service.CompanySearchService;
+import uk.gov.companieshouse.logging.Logger;
+import uk.gov.companieshouse.logging.LoggerFactory;
+
+@Service
+public class CompanySearchServiceImpl implements CompanySearchService {
+
+    private static final String COMPANY_SEARCH_URI = "/company-search/companies/%s";
+    private static final String COMPANY_PROFILE_URI = "/company/%s";
+    private final Supplier<InternalApiClient> internalApiClientSupplier;
+
+    private static final Logger LOG =
+            LoggerFactory.getLogger(String.valueOf(CompanySearchServiceImpl.class));
+
+    public CompanySearchServiceImpl(Supplier<InternalApiClient> internalApiClientSupplier) {
+        this.internalApiClientSupplier = internalApiClientSupplier;
+    }
+
+    @Override
+    public void addCompanyIntoElasticSearchIndex(CompanyData data)
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        String formattedCompanySearchUri = formatUri(COMPANY_SEARCH_URI, data.getCompanyNumber());
+        String formattedCompanyProfileUri = formatUri(COMPANY_PROFILE_URI, data.getCompanyNumber());
+        var companyProfileData = internalApiClientSupplier.get()
+                .privateCompanyResourceHandler()
+                .getCompanyFullProfile(formattedCompanyProfileUri).execute().getData();
+
+        try {
+            internalApiClientSupplier.get()
+                    .privateSearchResourceHandler()
+                    .companySearch()
+                    .upsertCompanyProfile(formattedCompanySearchUri, companyProfileData)
+                    .execute();
+            LOG.info("Company profile upsert is successful with company number: "
+                    + data.getCompanyNumber());
+        } catch (ApiErrorResponseException | URIValidationException ex) {
+            throw new DataException("Failed to upsert company profile: " + ex.getMessage());
+        }
+    }
+
+    @Override
+    public void deleteCompanyFromElasticSearchIndex(String companyNumber) throws DataException {
+        String formattedUri = formatUri(COMPANY_SEARCH_URI, companyNumber);
+        try {
+            internalApiClientSupplier.get()
+                    .privateSearchResourceHandler()
+                    .companySearch()
+                    .deleteCompanyProfile(formattedUri)
+                    .execute();
+        } catch (ApiErrorResponseException | URIValidationException ex) {
+            throw new DataException("Failed to upsert company profile: " + ex.getMessage());
+        }
+    }
+
+    private String formatUri(String template, String value) {
+        return String.format(template, value);
+    }
+}
diff --git a/src/main/java/uk/gov/companieshouse/api/testdata/service/impl/TestDataServiceImpl.java b/src/main/java/uk/gov/companieshouse/api/testdata/service/impl/TestDataServiceImpl.java
index bce658689..b088e6da1 100644
--- a/src/main/java/uk/gov/companieshouse/api/testdata/service/impl/TestDataServiceImpl.java
+++ b/src/main/java/uk/gov/companieshouse/api/testdata/service/impl/TestDataServiceImpl.java
@@ -13,7 +13,6 @@
 import uk.gov.companieshouse.api.testdata.exception.DataException;
 
 import uk.gov.companieshouse.api.testdata.model.entity.Appointment;
-import uk.gov.companieshouse.api.testdata.model.entity.CompanyAuthCode;
 import uk.gov.companieshouse.api.testdata.model.entity.CompanyMetrics;
 import uk.gov.companieshouse.api.testdata.model.entity.CompanyPscStatement;
 import uk.gov.companieshouse.api.testdata.model.entity.CompanyPscs;
@@ -28,7 +27,6 @@
 import uk.gov.companieshouse.api.testdata.model.rest.CompanySpec;
 import uk.gov.companieshouse.api.testdata.model.rest.IdentityData;
 import uk.gov.companieshouse.api.testdata.model.rest.IdentitySpec;
-import uk.gov.companieshouse.api.testdata.model.rest.Jurisdiction;
 import uk.gov.companieshouse.api.testdata.model.rest.RoleData;
 import uk.gov.companieshouse.api.testdata.model.rest.RoleSpec;
 import uk.gov.companieshouse.api.testdata.model.rest.UserData;
@@ -39,6 +37,7 @@
 import uk.gov.companieshouse.api.testdata.service.CompanyAuthAllowListService;
 import uk.gov.companieshouse.api.testdata.service.CompanyAuthCodeService;
 import uk.gov.companieshouse.api.testdata.service.CompanyProfileService;
+import uk.gov.companieshouse.api.testdata.service.CompanySearchService;
 import uk.gov.companieshouse.api.testdata.service.DataService;
 import uk.gov.companieshouse.api.testdata.service.RandomService;
 import uk.gov.companieshouse.api.testdata.service.TestDataService;
@@ -87,14 +86,23 @@ public class TestDataServiceImpl implements TestDataService {
     AppealsService appealsService;
     @Autowired
     private DataService<CompanyRegisters, CompanySpec> companyRegistersService;
+    @Autowired
+    private CompanySearchService companySearchService;
 
     @Value("${api.url}")
     private String apiUrl;
 
+    @Value("${elastic.search.deployed}")
+    private boolean isElasticSearchDeployed;
+
     void setAPIUrl(String apiUrl) {
         this.apiUrl = apiUrl;
     }
 
+    void setElasticSearchDeployed(Boolean isElasticSearchDeployed) {
+        this.isElasticSearchDeployed = isElasticSearchDeployed;
+    }
+
     @Override
     public CompanyData createCompanyData(final CompanySpec spec) throws DataException {
         if (spec == null) {
@@ -123,7 +131,15 @@ public CompanyData createCompanyData(final CompanySpec spec) throws DataExceptio
             }
 
             String companyUri = this.apiUrl + "/company/" + spec.getCompanyNumber();
-            return new CompanyData(spec.getCompanyNumber(), authCode.getAuthCode(), companyUri);
+
+            var companyData = new CompanyData(spec.getCompanyNumber(),
+                    authCode.getAuthCode(), companyUri);
+
+            // Add company to the elastic search index
+            if (isElasticSearchDeployed) {
+                this.companySearchService.addCompanyIntoElasticSearchIndex(companyData);
+            }
+            return companyData;
         } catch (Exception ex) {
             Map<String, Object> data = new HashMap<>();
             data.put("company number", spec.getCompanyNumber());
@@ -180,6 +196,14 @@ public void deleteCompanyData(String companyId) throws DataException {
             suppressedExceptions.add(de);
         }
 
+        if (isElasticSearchDeployed) {
+            try {
+                this.companySearchService.deleteCompanyFromElasticSearchIndex(companyId);
+            } catch (Exception de) {
+                suppressedExceptions.add(de);
+            }
+        }
+
         if (!suppressedExceptions.isEmpty()) {
             DataException ex = new DataException("Error deleting company data");
             suppressedExceptions.forEach(ex::addSuppressed);
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 0972a135a..d9bb5c450 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -8,4 +8,8 @@ barcode.url=${BARCODE_SERVICE_URL}
 
 api.url=${API_URL}
 
-logging.file.flush=true
\ No newline at end of file
+api-key=${CHS_API_KEY:testkey}
+
+logging.file.flush=true
+
+elastic.search.deployed=${ELASTIC_SEARCH_DEPLOYED:true}
\ No newline at end of file
diff --git a/src/test/java/uk/gov/companieshouse/api/testdata/config/AppConfigTest.java b/src/test/java/uk/gov/companieshouse/api/testdata/config/AppConfigTest.java
new file mode 100644
index 000000000..ec5d897d4
--- /dev/null
+++ b/src/test/java/uk/gov/companieshouse/api/testdata/config/AppConfigTest.java
@@ -0,0 +1,77 @@
+package uk.gov.companieshouse.api.testdata.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.*;
+
+import java.time.Clock;
+import java.util.function.Supplier;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.http.ApiKeyHttpClient;
+
+class AppConfigTest {
+
+    private static final String API_KEY = "test-api-key";
+    private static final String API_URL = "http://test-api-url";
+
+    private AppConfig appConfig;
+
+    @Mock
+    private ApiKeyHttpClient apiKeyHttpClient;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        appConfig = new AppConfig(API_KEY, API_URL);
+    }
+
+    @Test
+    void clock_ShouldReturnNonNullClock() {
+        Clock clock = appConfig.clock();
+        assertNotNull(clock);
+    }
+
+    @Test
+    void internalApiClientSupplier_ShouldReturnInternalApiClient() {
+        // Mock DataMapHolder behaviour
+        DataMapHolder.initialise("test-request-id");
+
+        Supplier<InternalApiClient> supplier = appConfig.internalApiClientSupplier();
+        InternalApiClient internalApiClient = supplier.get();
+
+        assertNotNull(internalApiClient);
+        assertEquals(API_URL, internalApiClient.getBasePath());
+
+        verify(apiKeyHttpClient, never()).setRequestId(anyString()); // Ensure no direct interaction
+    }
+
+    @Test
+    void internalApiClientSupplier_ShouldUseDataMapHolderGet() {
+        // Mock DataMapHolder behaviour
+        DataMapHolder.clear();
+        DataMapHolder.initialise("test-request-id");
+
+        Supplier<InternalApiClient> supplier = appConfig.internalApiClientSupplier();
+        InternalApiClient internalApiClient = supplier.get();
+
+        assertNotNull(internalApiClient);
+        verify(apiKeyHttpClient, never()).setRequestId(anyString());
+    }
+
+    @Test
+    void internalApiClientSupplier_ShouldClearDataMapHolder() {
+        // Mock DataMapHolder behaviour
+        DataMapHolder.initialise("test-request-id");
+
+        appConfig.internalApiClientSupplier().get();
+
+        // Verify that DataMapHolder.clear() is called
+        DataMapHolder.clear();
+        verifyNoInteractions(apiKeyHttpClient);
+    }
+}
diff --git a/src/test/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImplTest.java b/src/test/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImplTest.java
new file mode 100644
index 000000000..4e28df57d
--- /dev/null
+++ b/src/test/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImplTest.java
@@ -0,0 +1,160 @@
+package uk.gov.companieshouse.api.testdata.service.impl;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.function.Supplier;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.mockito.junit.jupiter.MockitoExtension;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.company.Data;
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.company.PrivateCompanyResourceHandler;
+import uk.gov.companieshouse.api.handler.company.request.PrivateCompanyFullProfileGet;
+import uk.gov.companieshouse.api.handler.search.PrivateSearchResourceHandler;
+import uk.gov.companieshouse.api.handler.search.company.PrivateCompanySearchHandler;
+import uk.gov.companieshouse.api.handler.search.company.request.PrivateCompanySearchDelete;
+import uk.gov.companieshouse.api.handler.search.company.request.PrivateCompanySearchUpsert;
+import uk.gov.companieshouse.api.model.ApiResponse;
+import uk.gov.companieshouse.api.testdata.exception.DataException;
+import uk.gov.companieshouse.api.testdata.model.rest.CompanyData;
+
+@ExtendWith(MockitoExtension.class)
+class CompanySearchServiceImplTest {
+
+    private static final ApiResponse<Void> SUCCESS_RESPONSE = new ApiResponse<>(200, null);
+    private static final String COMPANY_NUMBER = "12345678";
+    private static final String URI = "/company-search/companies/%s".formatted(COMPANY_NUMBER);
+
+    @Mock
+    private Supplier<InternalApiClient> internalApiClientSupplier;
+    @Mock
+    private InternalApiClient internalApiClient;
+    @Mock
+    private PrivateSearchResourceHandler privateSearchResourceHandler;
+    @Mock
+    private PrivateCompanySearchHandler privateCompanySearchHandler;
+    @Mock
+    private PrivateCompanySearchUpsert privateCompanySearchUpsert;
+    @Mock
+    private PrivateCompanySearchDelete privateCompanySearchDelete;
+    @Mock
+    private ApiResponse<Data> apiResponse;
+    @Mock
+    PrivateCompanyResourceHandler privateCompanyResourceHandler;
+    private CompanySearchServiceImpl service;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        when(internalApiClientSupplier.get()).thenReturn(internalApiClient);
+        service = new CompanySearchServiceImpl(internalApiClientSupplier);
+    }
+
+    @Test
+    void addCompanyIntoElasticSearchIndex_Success() throws Exception {
+        // given
+        PrivateCompanyFullProfileGet privateCompanyFullProfileGet
+                = mock(PrivateCompanyFullProfileGet.class);
+
+        // mock the ApiResponse
+        // ApiResponse<Data> apiResponse = mock(ApiResponse.class);
+
+        // Mocking the internalApiClient and its handlers
+        when(internalApiClientSupplier.get()).thenReturn(internalApiClient);
+        when(internalApiClient.privateCompanyResourceHandler())
+                .thenReturn(privateCompanyResourceHandler);
+        when(privateCompanyResourceHandler.getCompanyFullProfile(anyString()))
+                .thenReturn(privateCompanyFullProfileGet);
+        when(privateCompanyFullProfileGet.execute()).thenReturn(apiResponse);
+
+        // Mocking the search resource handler
+        when(internalApiClient.privateSearchResourceHandler())
+                .thenReturn(privateSearchResourceHandler);
+        when(privateSearchResourceHandler.companySearch()).thenReturn(privateCompanySearchHandler);
+        when(privateCompanySearchHandler.upsertCompanyProfile(anyString(), any()))
+                .thenReturn(privateCompanySearchUpsert);
+        when(privateCompanySearchUpsert.execute()).thenReturn(SUCCESS_RESPONSE);
+
+        // when
+        CompanyData companyData = new CompanyData(COMPANY_NUMBER, "authCode", "companyUri");
+        service.addCompanyIntoElasticSearchIndex(companyData);
+
+        // then
+        verify(privateCompanySearchHandler).upsertCompanyProfile(URI, apiResponse.getData());
+        verify(privateCompanySearchUpsert).execute();
+    }
+
+    @Test
+    void addCompanyIntoElasticSearchIndex_ShouldHandleNon200Response() throws Exception {
+        // given
+        PrivateCompanyFullProfileGet privateCompanyFullProfileGet
+                = mock(PrivateCompanyFullProfileGet.class);
+
+        // Mocking the internalApiClient and its handlers
+        when(internalApiClientSupplier.get()).thenReturn(internalApiClient);
+        when(internalApiClient.privateCompanyResourceHandler())
+                .thenReturn(privateCompanyResourceHandler);
+        when(privateCompanyResourceHandler.getCompanyFullProfile(anyString()))
+                .thenReturn(privateCompanyFullProfileGet);
+        when(privateCompanyFullProfileGet.execute()).thenReturn(apiResponse);
+
+        // given
+        when(internalApiClientSupplier.get()).thenReturn(internalApiClient);
+        when(internalApiClient.privateSearchResourceHandler())
+                .thenReturn(privateSearchResourceHandler);
+        when(privateSearchResourceHandler.companySearch()).thenReturn(privateCompanySearchHandler);
+        when(privateCompanySearchHandler.upsertCompanyProfile(anyString(), any())).thenReturn(
+                privateCompanySearchUpsert);
+        when(privateCompanySearchUpsert.execute()).thenThrow(ApiErrorResponseException.class);
+
+        // then
+        CompanyData companyData =
+                new CompanyData(COMPANY_NUMBER, "authCode", "companyUri");
+        assertThrows(DataException.class, () ->
+                service.addCompanyIntoElasticSearchIndex(companyData));
+    }
+
+    @Test
+    void shouldSuccessfullySendDeleteRequest() throws Exception {
+        // given
+        when(internalApiClient.privateSearchResourceHandler())
+                .thenReturn(privateSearchResourceHandler);
+        when(privateSearchResourceHandler.companySearch())
+                .thenReturn(privateCompanySearchHandler);
+        when(privateCompanySearchHandler.deleteCompanyProfile(URI))
+                .thenReturn(privateCompanySearchDelete);
+        when(privateCompanySearchDelete.execute()).thenReturn(SUCCESS_RESPONSE);
+
+        // when
+        service.deleteCompanyFromElasticSearchIndex(COMPANY_NUMBER);
+
+        // then
+        verify(privateCompanySearchHandler).deleteCompanyProfile(URI);
+    }
+
+    @Test
+    void deleteCompanyFromElasticSearchIndex_ShouldHandleNon200Response() throws Exception {
+        // given
+        when(internalApiClient.privateSearchResourceHandler())
+                .thenReturn(privateSearchResourceHandler);
+        when(privateSearchResourceHandler.companySearch()).thenReturn(privateCompanySearchHandler);
+        when(privateCompanySearchHandler.deleteCompanyProfile(URI))
+                .thenReturn(privateCompanySearchDelete);
+        when(privateCompanySearchDelete.execute()).thenThrow(ApiErrorResponseException.class);
+
+        // then
+        assertThrows(DataException.class, () ->
+                service.deleteCompanyFromElasticSearchIndex(COMPANY_NUMBER));
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/uk/gov/companieshouse/api/testdata/service/impl/TestDataServiceImplTest.java b/src/test/java/uk/gov/companieshouse/api/testdata/service/impl/TestDataServiceImplTest.java
index a372b2066..9629387ee 100644
--- a/src/test/java/uk/gov/companieshouse/api/testdata/service/impl/TestDataServiceImplTest.java
+++ b/src/test/java/uk/gov/companieshouse/api/testdata/service/impl/TestDataServiceImplTest.java
@@ -33,6 +33,8 @@
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.exception.URIValidationException;
 import uk.gov.companieshouse.api.testdata.exception.DataException;
 import uk.gov.companieshouse.api.testdata.model.entity.AcspMembers;
 import uk.gov.companieshouse.api.testdata.model.entity.Appointment;
@@ -65,6 +67,7 @@
 import uk.gov.companieshouse.api.testdata.service.CompanyAuthAllowListService;
 import uk.gov.companieshouse.api.testdata.service.CompanyAuthCodeService;
 import uk.gov.companieshouse.api.testdata.service.CompanyProfileService;
+import uk.gov.companieshouse.api.testdata.service.CompanySearchService;
 import uk.gov.companieshouse.api.testdata.service.DataService;
 import uk.gov.companieshouse.api.testdata.service.RandomService;
 import uk.gov.companieshouse.api.testdata.service.UserService;
@@ -81,26 +84,48 @@ class TestDataServiceImplTest {
     private static final String NI_COMPANY_PREFIX = "NI";
     private static final String API_URL = "http://localhost:4001";
 
-    @Mock private CompanyProfileService companyProfileService;
-    @Mock private DataService<FilingHistory, CompanySpec> filingHistoryService;
-    @Mock private CompanyAuthCodeService companyAuthCodeService;
-    @Mock private DataService<List<Appointment>, CompanySpec> appointmentService;
-    @Mock private DataService<CompanyMetrics, CompanySpec> metricsService;
-    @Mock private DataService<CompanyPscStatement, CompanySpec> companyPscStatementService;
-    @Mock private DataService<CompanyPscs, CompanySpec> companyPscsService;
-    @Mock private RandomService randomService;
-    @Mock private UserService userService;
-    @Mock private DataService<RoleData, RoleSpec> roleService;
-    @Mock private DataService<AcspMembersData, AcspMembersSpec> acspMembersService;
-    @InjectMocks private TestDataServiceImpl testDataService;
-    @Mock private AcspMembersRepository acspMembersRepository;
-    @Mock private DataService<AcspProfileData, AcspProfileSpec> acspProfileService;
-    @Captor private ArgumentCaptor<CompanySpec> specCaptor;
-    @Mock private DataService<IdentityData, IdentitySpec> identityService;
-    @Mock private CompanyAuthAllowListService companyAuthAllowListService;
-    @Mock private AppealsService appealsService;
-    @Mock private DataService<CompanyRegisters, CompanySpec> companyRegistersService;
-    @Mock private Appointment commonAppointment;
+    @Mock
+    private CompanyProfileService companyProfileService;
+    @Mock
+    private DataService<FilingHistory, CompanySpec> filingHistoryService;
+    @Mock
+    private CompanyAuthCodeService companyAuthCodeService;
+    @Mock
+    private DataService<List<Appointment>, CompanySpec> appointmentService;
+    @Mock
+    private DataService<CompanyMetrics, CompanySpec> metricsService;
+    @Mock
+    private DataService<CompanyPscStatement, CompanySpec> companyPscStatementService;
+    @Mock
+    private DataService<CompanyPscs, CompanySpec> companyPscsService;
+    @Mock
+    private RandomService randomService;
+    @Mock
+    private UserService userService;
+    @Mock
+    private DataService<RoleData, RoleSpec> roleService;
+    @Mock
+    private DataService<AcspMembersData, AcspMembersSpec> acspMembersService;
+    @InjectMocks
+    private TestDataServiceImpl testDataService;
+    @Mock
+    private AcspMembersRepository acspMembersRepository;
+    @Mock
+    private DataService<AcspProfileData, AcspProfileSpec> acspProfileService;
+    @Captor
+    private ArgumentCaptor<CompanySpec> specCaptor;
+    @Mock
+    private DataService<IdentityData, IdentitySpec> identityService;
+    @Mock
+    private CompanyAuthAllowListService companyAuthAllowListService;
+    @Mock
+    private AppealsService appealsService;
+    @Mock
+    private DataService<CompanyRegisters, CompanySpec> companyRegistersService;
+    @Mock
+    private Appointment commonAppointment;
+    @Mock
+    private CompanySearchService companySearchService;
 
     @BeforeEach
     void setUp() {
@@ -109,9 +134,10 @@ void setUp() {
 
     /**
      * Sets up common mocks for creating a company.
-     * @param spec the CompanySpec to be created.
-     * @param companyNumber the raw company number (as string) to be returned by randomService.
-     * @param numberDigits the number of digits to request from randomService.
+     *
+     * @param spec                      the CompanySpec to be created.
+     * @param companyNumber             the raw company number (as string) to be returned by randomService.
+     * @param numberDigits              the number of digits to request from randomService.
      * @param expectedFullCompanyNumber the full company number expected in the created spec.
      */
     private void setupCompanyCreationMocks(CompanySpec spec, String companyNumber, int numberDigits, String expectedFullCompanyNumber) throws DataException {
@@ -172,7 +198,7 @@ private void verifyCommonCompanyCreation(CompanySpec capturedSpec, CompanyData c
     /**
      * Helper to create ACSP members data.
      *
-     * @param userId the user id to set on the spec
+     * @param userId      the user id to set on the spec
      * @param profileData the ACSP profile data to be returned by the profile service
      * @param membersData the ACSP members data to be returned by the members service
      * @return the result of testDataService.createAcspMembersData(...)
@@ -206,7 +232,7 @@ private void verifyAcspMembersData(AcspMembersData data,
     /**
      * Helper to perform deletion of ACSP member data.
      *
-     * @param acspMemberId the member id to delete
+     * @param acspMemberId   the member id to delete
      * @param memberOptional an Optional containing the AcspMembers if found
      * @return the result of testDataService.deleteAcspMembersData(...)
      * @throws DataException if deletion fails
@@ -962,7 +988,7 @@ void createAcspMembersDataWhenProfileIsNotNull() throws DataException {
 
         verify(acspMembersService).create(argThat(membersSpec ->
                 acspMembersData.getUserId().equals(membersSpec.getUserId())
-                && acspMembersData.getAcspNumber().equals(membersSpec.getAcspNumber())
+                        && acspMembersData.getAcspNumber().equals(membersSpec.getAcspNumber())
         ));
     }
 
@@ -993,7 +1019,7 @@ void createAcspMembersDataWhenProfileIsNull() throws DataException {
 
         verify(acspMembersService).create(argThat(membersSpec ->
                 acspMembersData.getUserId().equals(membersSpec.getUserId())
-                && acspMembersData.getAcspNumber().equals(membersSpec.getAcspNumber())
+                        && acspMembersData.getAcspNumber().equals(membersSpec.getAcspNumber())
         ));
     }
 
@@ -1320,4 +1346,56 @@ void testUpdateUserWithOneLoginNotCalledOnException() throws DataException {
         assertEquals("Error creating identity", exception.getMessage());
         verify(userService, never()).updateUserWithOneLogin(anyString());
     }
+
+    @Test
+    void testCreateCompanyWithElasticSearchDeployed()
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        testCreateCompanyWithElasticSearch(true, 1);
+    }
+
+    @Test
+    void testCreateCompanyWithElasticSearchNotDeployed()
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        testCreateCompanyWithElasticSearch(false, 0);
+    }
+
+    private void testCreateCompanyWithElasticSearch(boolean isElasticSearchDeployed,
+                                                    int expectedInvocationCount)
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        testDataService.setElasticSearchDeployed(isElasticSearchDeployed);
+
+        CompanySpec spec = new CompanySpec();
+        spec.setJurisdiction(Jurisdiction.ENGLAND_WALES);
+        spec.setCompanyStatus("administration");
+        String expectedFullCompanyNumber = COMPANY_NUMBER;
+        setupCompanyCreationMocks(spec, COMPANY_NUMBER, 8, expectedFullCompanyNumber);
+
+        CompanyData createdCompany = testDataService.createCompanyData(spec);
+        CompanySpec capturedSpec = captureCompanySpec();
+        verifyCommonCompanyCreation(capturedSpec, createdCompany,
+                expectedFullCompanyNumber, Jurisdiction.ENGLAND_WALES);
+
+        verify(companySearchService, times(expectedInvocationCount))
+                .addCompanyIntoElasticSearchIndex(createdCompany);
+    }
+
+    @Test
+    void deleteCompanyDataWithElasticSearchDeployed()
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        testDataService.setElasticSearchDeployed(true);
+
+        testDataService.deleteCompanyData(COMPANY_NUMBER);
+
+        verify(companySearchService, times(1)).deleteCompanyFromElasticSearchIndex(COMPANY_NUMBER);
+    }
+
+    @Test
+    void deleteCompanyDataWithElasticSearchNotDeployed()
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        testDataService.setElasticSearchDeployed(false);
+
+        testDataService.deleteCompanyData(COMPANY_NUMBER);
+
+        verify(companySearchService, never()).deleteCompanyFromElasticSearchIndex(COMPANY_NUMBER);
+    }
 }
\ No newline at end of file
diff --git a/version b/version
index 9a7d84f2a..9f8e9b69a 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-0.9
\ No newline at end of file
+1.0
\ No newline at end of file


Reviews:
user: login: Joseph-Nash
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T07:00:30Z
user: login: Joseph-Nash
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T07:03:02Z
user: login: lyadavalli
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T10:18:52Z
user: login: lyadavalli
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T10:18:59Z
user: login: Joseph-Nash
body: LGTM but I would wait for another approval before merging 👍 
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T10:26:58Z
user: login: bcullerton
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T15:07:26Z
user: login: lyadavalli
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T10:35:11Z
user: login: lyadavalli
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T10:35:27Z
user: login: lyadavalli
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T10:35:51Z
user: login: lyadavalli
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T10:35:59Z
user: login: bcullerton
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T10:46:45Z
user: login: bcullerton
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T11:01:13Z
user: login: kuzonduch
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T11:34:10Z
user: login: Joseph-Nash
state: APPROVED
author_association: NONE
submitted_at: 2025-04-25T12:25:45Z

Review comments:
diff_hunk: @@ -0,0 +1,107 @@
+package uk.gov.companieshouse.api.testdata.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import java.util.function.Supplier;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.company.Data;
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.exception.URIValidationException;
+
+import uk.gov.companieshouse.api.model.company.CompanyProfileApi;
+import uk.gov.companieshouse.api.testdata.exception.DataException;
+import uk.gov.companieshouse.api.testdata.model.rest.CompanyData;
+import uk.gov.companieshouse.api.testdata.service.CompanySearchService;
+import uk.gov.companieshouse.logging.Logger;
+import uk.gov.companieshouse.logging.LoggerFactory;
+
+@Service
+public class CompanySearchServiceImpl implements CompanySearchService {
+
+    private static final String COMPANY_SEARCH_URI = "/company-search/companies/%s";
+    private static final String COMPANY_PROFILE_URI = "/company/%s";
+    private final Supplier<InternalApiClient> internalApiClientSupplier;
+
+    @Autowired
+    private final ObjectMapper objectMapper;
+
+    private static final Logger LOG =
+            LoggerFactory.getLogger(String.valueOf(CompanyProfileServiceImpl.class));
+
+    public CompanySearchServiceImpl(Supplier<InternalApiClient> internalApiClientSupplier,
+                                    ObjectMapper objectMapper) {
+        this.internalApiClientSupplier = internalApiClientSupplier;
+        this.objectMapper = objectMapper;
+        this.objectMapper.registerModule(new JavaTimeModule());
+        this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    }
+
+    @Override
+    public void addCompanyIntoElasticSearchIndex(CompanyData data)
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        String formattedUri = formatUri(COMPANY_SEARCH_URI, data.getCompanyNumber());
+        var companyProfileApi = getCompanyProfile(data.getCompanyNumber());
+        String companyProfileJson = serializeCompanyProfile(companyProfileApi);
+
+        var companyProfileData = deserializeCompanyProfile(companyProfileJson);
+        try {
+            internalApiClientSupplier.get()
+                    .privateSearchResourceHandler()
+                    .companySearch()
+                    .upsertCompanyProfile(formattedUri, companyProfileData)
+                    .execute();
+            LOG.info("Company profile upsert is successful with company number: "
+                    + data.getCompanyNumber());
+        } catch (ApiErrorResponseException | URIValidationException ex) {
+            throw new DataException("Failed to upsert company profile: " + ex.getMessage());
+        }
+    }
+
+    @Override
+    public void deleteCompanyFromElasticSearchIndex(String companyNumber) throws DataException {
+        String formattedUri = formatUri(COMPANY_SEARCH_URI, companyNumber);
+        try {
+            internalApiClientSupplier.get()
+                    .privateSearchResourceHandler()
+                    .companySearch()
+                    .deleteCompanyProfile(formattedUri)
+                    .execute();
+        } catch (ApiErrorResponseException | URIValidationException ex) {
+            throw new DataException("Failed to upsert company profile: " + ex.getMessage());
+        }
+    }
+
+    private CompanyProfileApi getCompanyProfile(String companyNumber)
+            throws ApiErrorResponseException, URIValidationException {
+        String uri = formatUri(COMPANY_PROFILE_URI, companyNumber);
+        var companyProfileApiApiResponse
path: src/main/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImpl.java
user: login: Joseph-Nash
body: ApiApi? 
created_at: 2025-04-23T07:00:30Z
author_association: NONE
diff_hunk: @@ -0,0 +1,77 @@
+package uk.gov.companieshouse.api.testdata.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.*;
+
+import java.time.Clock;
+import java.util.function.Supplier;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.http.ApiKeyHttpClient;
+
+class AppConfigTest {
+
+    private static final String API_KEY = "test-api-key";
+    private static final String API_URL = "http://test-api-url";
+
+    private AppConfig appConfig;
+
+    @Mock
+    private ApiKeyHttpClient apiKeyHttpClient;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        appConfig = new AppConfig(API_KEY, API_URL);
+    }
+
+    @Test
+    void clock_ShouldReturnNonNullClock() {
+        Clock clock = appConfig.clock();
+        assertNotNull(clock);
+    }
+
+    @Test
+    void internalApiClientSupplier_ShouldReturnInternalApiClient() {
+        // Mock DataMapHolder behavior
path: src/test/java/uk/gov/companieshouse/api/testdata/config/AppConfigTest.java
user: login: Joseph-Nash
body: *behaviour, same for a few below
created_at: 2025-04-23T07:03:02Z
author_association: NONE
diff_hunk: @@ -0,0 +1,107 @@
+package uk.gov.companieshouse.api.testdata.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import java.util.function.Supplier;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.company.Data;
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.exception.URIValidationException;
+
+import uk.gov.companieshouse.api.model.company.CompanyProfileApi;
+import uk.gov.companieshouse.api.testdata.exception.DataException;
+import uk.gov.companieshouse.api.testdata.model.rest.CompanyData;
+import uk.gov.companieshouse.api.testdata.service.CompanySearchService;
+import uk.gov.companieshouse.logging.Logger;
+import uk.gov.companieshouse.logging.LoggerFactory;
+
+@Service
+public class CompanySearchServiceImpl implements CompanySearchService {
+
+    private static final String COMPANY_SEARCH_URI = "/company-search/companies/%s";
+    private static final String COMPANY_PROFILE_URI = "/company/%s";
+    private final Supplier<InternalApiClient> internalApiClientSupplier;
+
+    @Autowired
+    private final ObjectMapper objectMapper;
+
+    private static final Logger LOG =
+            LoggerFactory.getLogger(String.valueOf(CompanyProfileServiceImpl.class));
+
+    public CompanySearchServiceImpl(Supplier<InternalApiClient> internalApiClientSupplier,
+                                    ObjectMapper objectMapper) {
+        this.internalApiClientSupplier = internalApiClientSupplier;
+        this.objectMapper = objectMapper;
+        this.objectMapper.registerModule(new JavaTimeModule());
+        this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    }
+
+    @Override
+    public void addCompanyIntoElasticSearchIndex(CompanyData data)
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        String formattedUri = formatUri(COMPANY_SEARCH_URI, data.getCompanyNumber());
+        var companyProfileApi = getCompanyProfile(data.getCompanyNumber());
+        String companyProfileJson = serializeCompanyProfile(companyProfileApi);
+
+        var companyProfileData = deserializeCompanyProfile(companyProfileJson);
+        try {
+            internalApiClientSupplier.get()
+                    .privateSearchResourceHandler()
+                    .companySearch()
+                    .upsertCompanyProfile(formattedUri, companyProfileData)
+                    .execute();
+            LOG.info("Company profile upsert is successful with company number: "
+                    + data.getCompanyNumber());
+        } catch (ApiErrorResponseException | URIValidationException ex) {
+            throw new DataException("Failed to upsert company profile: " + ex.getMessage());
+        }
+    }
+
+    @Override
+    public void deleteCompanyFromElasticSearchIndex(String companyNumber) throws DataException {
+        String formattedUri = formatUri(COMPANY_SEARCH_URI, companyNumber);
+        try {
+            internalApiClientSupplier.get()
+                    .privateSearchResourceHandler()
+                    .companySearch()
+                    .deleteCompanyProfile(formattedUri)
+                    .execute();
+        } catch (ApiErrorResponseException | URIValidationException ex) {
+            throw new DataException("Failed to upsert company profile: " + ex.getMessage());
+        }
+    }
+
+    private CompanyProfileApi getCompanyProfile(String companyNumber)
+            throws ApiErrorResponseException, URIValidationException {
+        String uri = formatUri(COMPANY_PROFILE_URI, companyNumber);
+        var companyProfileApiApiResponse
path: src/main/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImpl.java
user: login: lyadavalli
body: updated
created_at: 2025-04-23T10:18:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,77 @@
+package uk.gov.companieshouse.api.testdata.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.*;
+
+import java.time.Clock;
+import java.util.function.Supplier;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.http.ApiKeyHttpClient;
+
+class AppConfigTest {
+
+    private static final String API_KEY = "test-api-key";
+    private static final String API_URL = "http://test-api-url";
+
+    private AppConfig appConfig;
+
+    @Mock
+    private ApiKeyHttpClient apiKeyHttpClient;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        appConfig = new AppConfig(API_KEY, API_URL);
+    }
+
+    @Test
+    void clock_ShouldReturnNonNullClock() {
+        Clock clock = appConfig.clock();
+        assertNotNull(clock);
+    }
+
+    @Test
+    void internalApiClientSupplier_ShouldReturnInternalApiClient() {
+        // Mock DataMapHolder behavior
path: src/test/java/uk/gov/companieshouse/api/testdata/config/AppConfigTest.java
user: login: lyadavalli
body: updated
created_at: 2025-04-23T10:18:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,107 @@
+package uk.gov.companieshouse.api.testdata.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import java.util.function.Supplier;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.company.Data;
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.exception.URIValidationException;
+
+import uk.gov.companieshouse.api.model.company.CompanyProfileApi;
+import uk.gov.companieshouse.api.testdata.exception.DataException;
+import uk.gov.companieshouse.api.testdata.model.rest.CompanyData;
+import uk.gov.companieshouse.api.testdata.service.CompanySearchService;
+import uk.gov.companieshouse.logging.Logger;
+import uk.gov.companieshouse.logging.LoggerFactory;
+
+@Service
+public class CompanySearchServiceImpl implements CompanySearchService {
+
+    private static final String COMPANY_SEARCH_URI = "/company-search/companies/%s";
+    private static final String COMPANY_PROFILE_URI = "/company/%s";
+    private final Supplier<InternalApiClient> internalApiClientSupplier;
+
+    @Autowired
+    private final ObjectMapper objectMapper;
+
+    private static final Logger LOG =
+            LoggerFactory.getLogger(String.valueOf(CompanyProfileServiceImpl.class));
path: src/main/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImpl.java
user: login: bcullerton
body: Should this be a logger for Search rather than Profile service?
created_at: 2025-04-24T14:33:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,107 @@
+package uk.gov.companieshouse.api.testdata.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import java.util.function.Supplier;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.company.Data;
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.exception.URIValidationException;
+
+import uk.gov.companieshouse.api.model.company.CompanyProfileApi;
+import uk.gov.companieshouse.api.testdata.exception.DataException;
+import uk.gov.companieshouse.api.testdata.model.rest.CompanyData;
+import uk.gov.companieshouse.api.testdata.service.CompanySearchService;
+import uk.gov.companieshouse.logging.Logger;
+import uk.gov.companieshouse.logging.LoggerFactory;
+
+@Service
+public class CompanySearchServiceImpl implements CompanySearchService {
+
+    private static final String COMPANY_SEARCH_URI = "/company-search/companies/%s";
+    private static final String COMPANY_PROFILE_URI = "/company/%s";
+    private final Supplier<InternalApiClient> internalApiClientSupplier;
+
+    @Autowired
+    private final ObjectMapper objectMapper;
+
+    private static final Logger LOG =
+            LoggerFactory.getLogger(String.valueOf(CompanyProfileServiceImpl.class));
+
+    public CompanySearchServiceImpl(Supplier<InternalApiClient> internalApiClientSupplier,
+                                    ObjectMapper objectMapper) {
+        this.internalApiClientSupplier = internalApiClientSupplier;
+        this.objectMapper = objectMapper;
+        this.objectMapper.registerModule(new JavaTimeModule());
+        this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    }
+
+    @Override
+    public void addCompanyIntoElasticSearchIndex(CompanyData data)
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        String formattedUri = formatUri(COMPANY_SEARCH_URI, data.getCompanyNumber());
+        var companyProfileApi = getCompanyProfile(data.getCompanyNumber());
+        String companyProfileJson = serializeCompanyProfile(companyProfileApi);
+
+        var companyProfileData = deserializeCompanyProfile(companyProfileJson);
path: src/main/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImpl.java
user: login: bcullerton
body: There's already an sdk function that returns the Data object from the company-profile endpoint. Rather than mapping the response to the Data object you can just use 

internalApiClientSupplier.get().privateCompanyResourceHandler().getCompanyFullProfile(uri).execute();

https://github.com/companieshouse/private-api-sdk-java/blob/main/src/main/java/uk/gov/companieshouse/api/handler/company/request/PrivateCompanyFullProfileGet.java#L26
created_at: 2025-04-24T14:42:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,107 @@
+package uk.gov.companieshouse.api.testdata.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import java.util.function.Supplier;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.company.Data;
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.exception.URIValidationException;
+
+import uk.gov.companieshouse.api.model.company.CompanyProfileApi;
+import uk.gov.companieshouse.api.testdata.exception.DataException;
+import uk.gov.companieshouse.api.testdata.model.rest.CompanyData;
+import uk.gov.companieshouse.api.testdata.service.CompanySearchService;
+import uk.gov.companieshouse.logging.Logger;
+import uk.gov.companieshouse.logging.LoggerFactory;
+
+@Service
+public class CompanySearchServiceImpl implements CompanySearchService {
+
+    private static final String COMPANY_SEARCH_URI = "/company-search/companies/%s";
+    private static final String COMPANY_PROFILE_URI = "/company/%s";
+    private final Supplier<InternalApiClient> internalApiClientSupplier;
+
+    @Autowired
+    private final ObjectMapper objectMapper;
+
+    private static final Logger LOG =
+            LoggerFactory.getLogger(String.valueOf(CompanyProfileServiceImpl.class));
+
+    public CompanySearchServiceImpl(Supplier<InternalApiClient> internalApiClientSupplier,
+                                    ObjectMapper objectMapper) {
+        this.internalApiClientSupplier = internalApiClientSupplier;
+        this.objectMapper = objectMapper;
+        this.objectMapper.registerModule(new JavaTimeModule());
+        this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    }
+
+    @Override
+    public void addCompanyIntoElasticSearchIndex(CompanyData data)
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        String formattedUri = formatUri(COMPANY_SEARCH_URI, data.getCompanyNumber());
+        var companyProfileApi = getCompanyProfile(data.getCompanyNumber());
+        String companyProfileJson = serializeCompanyProfile(companyProfileApi);
+
+        var companyProfileData = deserializeCompanyProfile(companyProfileJson);
+        try {
+            internalApiClientSupplier.get()
+                    .privateSearchResourceHandler()
+                    .companySearch()
+                    .upsertCompanyProfile(formattedUri, companyProfileData)
+                    .execute();
+            LOG.info("Company profile upsert is successful with company number: "
+                    + data.getCompanyNumber());
+        } catch (ApiErrorResponseException | URIValidationException ex) {
+            throw new DataException("Failed to upsert company profile: " + ex.getMessage());
+        }
+    }
+
+    @Override
+    public void deleteCompanyFromElasticSearchIndex(String companyNumber) throws DataException {
+        String formattedUri = formatUri(COMPANY_SEARCH_URI, companyNumber);
+        try {
+            internalApiClientSupplier.get()
+                    .privateSearchResourceHandler()
+                    .companySearch()
+                    .deleteCompanyProfile(formattedUri)
+                    .execute();
+        } catch (ApiErrorResponseException | URIValidationException ex) {
+            throw new DataException("Failed to upsert company profile: " + ex.getMessage());
+        }
+    }
+
+    private CompanyProfileApi getCompanyProfile(String companyNumber)
+            throws ApiErrorResponseException, URIValidationException {
+        String uri = formatUri(COMPANY_PROFILE_URI, companyNumber);
+        var companyProfileApiResponse
+                = internalApiClientSupplier.get().company().get(uri).execute();
path: src/main/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImpl.java
user: login: bcullerton
body: See above comment
created_at: 2025-04-24T15:03:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -179,6 +195,14 @@ public void deleteCompanyData(String companyId) throws DataException {
         } catch (Exception de) {
             suppressedExceptions.add(de);
         }
+        try {
+            // Delete company to the elastic search index
+            if (isElasticSearchDeployed) {
path: src/main/java/uk/gov/companieshouse/api/testdata/service/impl/TestDataServiceImpl.java
user: login: bcullerton
body: Should this if and try be the other way around? Only enter the try if we have to
created_at: 2025-04-24T15:04:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,107 @@
+package uk.gov.companieshouse.api.testdata.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import java.util.function.Supplier;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.company.Data;
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.exception.URIValidationException;
+
+import uk.gov.companieshouse.api.model.company.CompanyProfileApi;
+import uk.gov.companieshouse.api.testdata.exception.DataException;
+import uk.gov.companieshouse.api.testdata.model.rest.CompanyData;
+import uk.gov.companieshouse.api.testdata.service.CompanySearchService;
+import uk.gov.companieshouse.logging.Logger;
+import uk.gov.companieshouse.logging.LoggerFactory;
+
+@Service
+public class CompanySearchServiceImpl implements CompanySearchService {
+
+    private static final String COMPANY_SEARCH_URI = "/company-search/companies/%s";
+    private static final String COMPANY_PROFILE_URI = "/company/%s";
+    private final Supplier<InternalApiClient> internalApiClientSupplier;
+
+    @Autowired
+    private final ObjectMapper objectMapper;
+
+    private static final Logger LOG =
+            LoggerFactory.getLogger(String.valueOf(CompanyProfileServiceImpl.class));
path: src/main/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImpl.java
user: login: lyadavalli
body: updated
created_at: 2025-04-25T10:35:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,107 @@
+package uk.gov.companieshouse.api.testdata.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import java.util.function.Supplier;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.company.Data;
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.exception.URIValidationException;
+
+import uk.gov.companieshouse.api.model.company.CompanyProfileApi;
+import uk.gov.companieshouse.api.testdata.exception.DataException;
+import uk.gov.companieshouse.api.testdata.model.rest.CompanyData;
+import uk.gov.companieshouse.api.testdata.service.CompanySearchService;
+import uk.gov.companieshouse.logging.Logger;
+import uk.gov.companieshouse.logging.LoggerFactory;
+
+@Service
+public class CompanySearchServiceImpl implements CompanySearchService {
+
+    private static final String COMPANY_SEARCH_URI = "/company-search/companies/%s";
+    private static final String COMPANY_PROFILE_URI = "/company/%s";
+    private final Supplier<InternalApiClient> internalApiClientSupplier;
+
+    @Autowired
+    private final ObjectMapper objectMapper;
+
+    private static final Logger LOG =
+            LoggerFactory.getLogger(String.valueOf(CompanyProfileServiceImpl.class));
+
+    public CompanySearchServiceImpl(Supplier<InternalApiClient> internalApiClientSupplier,
+                                    ObjectMapper objectMapper) {
+        this.internalApiClientSupplier = internalApiClientSupplier;
+        this.objectMapper = objectMapper;
+        this.objectMapper.registerModule(new JavaTimeModule());
+        this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    }
+
+    @Override
+    public void addCompanyIntoElasticSearchIndex(CompanyData data)
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        String formattedUri = formatUri(COMPANY_SEARCH_URI, data.getCompanyNumber());
+        var companyProfileApi = getCompanyProfile(data.getCompanyNumber());
+        String companyProfileJson = serializeCompanyProfile(companyProfileApi);
+
+        var companyProfileData = deserializeCompanyProfile(companyProfileJson);
path: src/main/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImpl.java
user: login: lyadavalli
body: code refactored based on this
created_at: 2025-04-25T10:35:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,107 @@
+package uk.gov.companieshouse.api.testdata.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import java.util.function.Supplier;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import uk.gov.companieshouse.api.InternalApiClient;
+import uk.gov.companieshouse.api.company.Data;
+import uk.gov.companieshouse.api.error.ApiErrorResponseException;
+import uk.gov.companieshouse.api.handler.exception.URIValidationException;
+
+import uk.gov.companieshouse.api.model.company.CompanyProfileApi;
+import uk.gov.companieshouse.api.testdata.exception.DataException;
+import uk.gov.companieshouse.api.testdata.model.rest.CompanyData;
+import uk.gov.companieshouse.api.testdata.service.CompanySearchService;
+import uk.gov.companieshouse.logging.Logger;
+import uk.gov.companieshouse.logging.LoggerFactory;
+
+@Service
+public class CompanySearchServiceImpl implements CompanySearchService {
+
+    private static final String COMPANY_SEARCH_URI = "/company-search/companies/%s";
+    private static final String COMPANY_PROFILE_URI = "/company/%s";
+    private final Supplier<InternalApiClient> internalApiClientSupplier;
+
+    @Autowired
+    private final ObjectMapper objectMapper;
+
+    private static final Logger LOG =
+            LoggerFactory.getLogger(String.valueOf(CompanyProfileServiceImpl.class));
+
+    public CompanySearchServiceImpl(Supplier<InternalApiClient> internalApiClientSupplier,
+                                    ObjectMapper objectMapper) {
+        this.internalApiClientSupplier = internalApiClientSupplier;
+        this.objectMapper = objectMapper;
+        this.objectMapper.registerModule(new JavaTimeModule());
+        this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    }
+
+    @Override
+    public void addCompanyIntoElasticSearchIndex(CompanyData data)
+            throws DataException, ApiErrorResponseException, URIValidationException {
+        String formattedUri = formatUri(COMPANY_SEARCH_URI, data.getCompanyNumber());
+        var companyProfileApi = getCompanyProfile(data.getCompanyNumber());
+        String companyProfileJson = serializeCompanyProfile(companyProfileApi);
+
+        var companyProfileData = deserializeCompanyProfile(companyProfileJson);
+        try {
+            internalApiClientSupplier.get()
+                    .privateSearchResourceHandler()
+                    .companySearch()
+                    .upsertCompanyProfile(formattedUri, companyProfileData)
+                    .execute();
+            LOG.info("Company profile upsert is successful with company number: "
+                    + data.getCompanyNumber());
+        } catch (ApiErrorResponseException | URIValidationException ex) {
+            throw new DataException("Failed to upsert company profile: " + ex.getMessage());
+        }
+    }
+
+    @Override
+    public void deleteCompanyFromElasticSearchIndex(String companyNumber) throws DataException {
+        String formattedUri = formatUri(COMPANY_SEARCH_URI, companyNumber);
+        try {
+            internalApiClientSupplier.get()
+                    .privateSearchResourceHandler()
+                    .companySearch()
+                    .deleteCompanyProfile(formattedUri)
+                    .execute();
+        } catch (ApiErrorResponseException | URIValidationException ex) {
+            throw new DataException("Failed to upsert company profile: " + ex.getMessage());
+        }
+    }
+
+    private CompanyProfileApi getCompanyProfile(String companyNumber)
+            throws ApiErrorResponseException, URIValidationException {
+        String uri = formatUri(COMPANY_PROFILE_URI, companyNumber);
+        var companyProfileApiResponse
+                = internalApiClientSupplier.get().company().get(uri).execute();
path: src/main/java/uk/gov/companieshouse/api/testdata/service/impl/CompanySearchServiceImpl.java
user: login: lyadavalli
body: code refactored as per the suggestion
created_at: 2025-04-25T10:35:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -179,6 +195,14 @@ public void deleteCompanyData(String companyId) throws DataException {
         } catch (Exception de) {
             suppressedExceptions.add(de);
         }
+        try {
+            // Delete company to the elastic search index
+            if (isElasticSearchDeployed) {
path: src/main/java/uk/gov/companieshouse/api/testdata/service/impl/TestDataServiceImpl.java
user: login: lyadavalli
body: updated
created_at: 2025-04-25T10:35:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -81,6 +83,12 @@
             <artifactId>jackson-annotations</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+            <version>2.6.0</version>
+        </dependency>
path: pom.xml
user: login: bcullerton
body: Do we still need this dependency now we're not using an object mapper?
created_at: 2025-04-25T10:45:07Z
author_association: CONTRIBUTOR

PR comments:
user: login: chsonarqubeprchecks[bot]
created_at: 2025-04-25T11:19:09Z
body: **SonarQube Quality Gate**

[![Quality Gate passed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v86/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](http://code-analysis.platform.aws.chdev.org:9000/dashboard?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140)

[![Bug](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v86/common/bug-16px.png 'Bug')](http://code-analysis.platform.aws.chdev.org:9000/project/issues?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&resolved=false&types=BUG) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v86/checks/RatingBadge/A-16px.png 'A')](http://code-analysis.platform.aws.chdev.org:9000/project/issues?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&resolved=false&types=BUG) [0 Bugs](http://code-analysis.platform.aws.chdev.org:9000/project/issues?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&resolved=false&types=BUG)  
[![Vulnerability](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v86/common/vulnerability-16px.png 'Vulnerability')](http://code-analysis.platform.aws.chdev.org:9000/project/issues?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&resolved=false&types=VULNERABILITY) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v86/checks/RatingBadge/A-16px.png 'A')](http://code-analysis.platform.aws.chdev.org:9000/project/issues?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](http://code-analysis.platform.aws.chdev.org:9000/project/issues?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v86/common/security_hotspot-16px.png 'Security Hotspot')](http://code-analysis.platform.aws.chdev.org:9000/security_hotspots?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v86/checks/RatingBadge/A-16px.png 'A')](http://code-analysis.platform.aws.chdev.org:9000/security_hotspots?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140) [0 Security Hotspots](http://code-analysis.platform.aws.chdev.org:9000/security_hotspots?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140)  
[![Code Smell](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v86/common/code_smell-16px.png 'Code Smell')](http://code-analysis.platform.aws.chdev.org:9000/project/issues?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&resolved=false&types=CODE_SMELL) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v86/checks/RatingBadge/A-16px.png 'A')](http://code-analysis.platform.aws.chdev.org:9000/project/issues?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&resolved=false&types=CODE_SMELL) [0 Code Smells](http://code-analysis.platform.aws.chdev.org:9000/project/issues?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&resolved=false&types=CODE_SMELL)

[![95.7%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v86/checks/CoverageChart/90-16px.png '95.7%')](http://code-analysis.platform.aws.chdev.org:9000/component_measures?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&metric=new_coverage&view=list) [95.7% Coverage](http://code-analysis.platform.aws.chdev.org:9000/component_measures?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&metric=new_coverage&view=list)  
[![0.0%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v86/checks/Duplications/3-16px.png '0.0%')](http://code-analysis.platform.aws.chdev.org:9000/component_measures?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](http://code-analysis.platform.aws.chdev.org:9000/component_measures?id=uk.gov.companieshouse%3Atest-data-generator&pullRequest=140&metric=new_duplicated_lines_density&view=list)


author_association: NONE