Skip to content

Commit 70aefac

Browse files
authored
Merge pull request #19 from companieshouse/feature/call-to-resource-changed-endpoint
DSND-1632: added a call to resource-changed endpoint
2 parents c14d636 + 3f6ec68 commit 70aefac

File tree

12 files changed

+326
-6
lines changed

12 files changed

+326
-6
lines changed

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,13 @@
193193
<groupId>org.apache.maven.plugins</groupId>
194194
<artifactId>maven-surefire-plugin</artifactId>
195195
<version>${maven-surefire-plugin.version}</version>
196+
<configuration>
197+
<environmentVariables>
198+
<CHS_API_KEY>key</CHS_API_KEY>
199+
<API_URL>key</API_URL>
200+
<PAYMENTS_API_URL>key</PAYMENTS_API_URL>
201+
</environmentVariables>
202+
</configuration>
196203
</plugin>
197204
<plugin>
198205
<groupId>org.apache.maven.plugins</groupId>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package uk.gov.companieshouse.pscdataapi.api;
2+
3+
import java.time.OffsetDateTime;
4+
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.stereotype.Service;
8+
import uk.gov.companieshouse.api.InternalApiClient;
9+
import uk.gov.companieshouse.api.chskafka.ChangedResource;
10+
import uk.gov.companieshouse.api.chskafka.ChangedResourceEvent;
11+
import uk.gov.companieshouse.api.error.ApiErrorResponseException;
12+
import uk.gov.companieshouse.api.handler.chskafka.request.PrivateChangedResourcePost;
13+
import uk.gov.companieshouse.api.model.ApiResponse;
14+
import uk.gov.companieshouse.logging.Logger;
15+
import uk.gov.companieshouse.pscdataapi.exceptions.ServiceUnavailableException;
16+
import uk.gov.companieshouse.pscdataapi.util.PscTransformationHelper;
17+
18+
@Service
19+
public class ChsKafkaApiService {
20+
@Autowired
21+
InternalApiClient internalApiClient;
22+
@Value("${chs.api.kafka.url}")
23+
private String chsKafkaApiUrl;
24+
@Value("${chs.api.kafka.resource-changed.uri}")
25+
private String resourceChangedUri;
26+
private static final String PSC_URI = "/company/%s/persons-with-significant-control/"
27+
+ "%s/full_record";
28+
private static final String CHANGED_EVENT_TYPE = "changed";
29+
@Autowired
30+
private Logger logger;
31+
32+
/**
33+
* Creates a ChangedResource object to send a request to the chs kafka api.
34+
*
35+
* @param contextId chs kafka id
36+
* @param companyNumber company number of psc
37+
* @param notificationId mongo id
38+
* @return passes request to api response handling
39+
*/
40+
public ApiResponse<Void> invokeChsKafkaApi(String contextId, String companyNumber,
41+
String notificationId, String kind) {
42+
internalApiClient.setBasePath(chsKafkaApiUrl);
43+
PrivateChangedResourcePost changedResourcePost = internalApiClient
44+
.privateChangedResourceHandler().postChangedResource(resourceChangedUri,
45+
mapChangedResource(contextId, companyNumber, notificationId, kind));
46+
return handleApiCall(changedResourcePost);
47+
}
48+
49+
private ChangedResource mapChangedResource(String contextId, String companyNumber,
50+
String notificationId, String kind) {
51+
ChangedResourceEvent event = new ChangedResourceEvent();
52+
ChangedResource changedResource = new ChangedResource();
53+
event.setPublishedAt(String.valueOf(OffsetDateTime.now()));
54+
event.setType(CHANGED_EVENT_TYPE);
55+
changedResource.setResourceUri(String.format(PSC_URI, companyNumber, notificationId));
56+
changedResource.event(event);
57+
changedResource.setResourceKind(PscTransformationHelper.mapResourceKind(kind));
58+
changedResource.setContextId(contextId);
59+
return changedResource;
60+
}
61+
62+
private ApiResponse<Void> handleApiCall(PrivateChangedResourcePost changedResourcePost) {
63+
try {
64+
return changedResourcePost.execute();
65+
} catch (ApiErrorResponseException exception) {
66+
logger.error("Unsuccessful call to /resource-changed endpoint", exception);
67+
throw new ServiceUnavailableException(exception.getMessage());
68+
} catch (RuntimeException exception) {
69+
logger.error("Error occurred while calling /resource-changed endpoint", exception);
70+
throw exception;
71+
}
72+
}
73+
}

src/main/java/uk/gov/companieshouse/pscdataapi/config/ApplicationConfig.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
import org.springframework.context.annotation.Bean;
1414
import org.springframework.context.annotation.Configuration;
1515
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
16+
import uk.gov.companieshouse.api.InternalApiClient;
1617
import uk.gov.companieshouse.pscdataapi.converter.CompanyPscReadConverter;
1718
import uk.gov.companieshouse.pscdataapi.converter.CompanyPscWriteConverter;
1819
import uk.gov.companieshouse.pscdataapi.serialization.LocalDateDeSerializer;
1920
import uk.gov.companieshouse.pscdataapi.serialization.LocalDateSerializer;
21+
import uk.gov.companieshouse.sdk.manager.ApiSdkManager;
2022

2123
@Configuration
2224
public class ApplicationConfig {
@@ -33,6 +35,11 @@ public MongoCustomConversions mongoCustomConversions() {
3335
new CompanyPscReadConverter(objectMapper)));
3436
}
3537

38+
@Bean
39+
public InternalApiClient internalApiClient() {
40+
return ApiSdkManager.getPrivateSDK();
41+
}
42+
3643
@Bean
3744
public Supplier<String> offsetDateTimeGenerator() {
3845
return () -> String.valueOf(OffsetDateTime.now());

src/main/java/uk/gov/companieshouse/pscdataapi/config/ExceptionHandlerConfig.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package uk.gov.companieshouse.pscdataapi.config;
22

3+
import com.mongodb.MongoException;
34
import java.time.LocalDateTime;
45
import java.time.format.DateTimeParseException;
56
import java.util.LinkedHashMap;
@@ -98,7 +99,8 @@ public ResponseEntity<Object> handleMethodNotAllowedException(
9899
* @param request request.
99100
* @return error response to return.
100101
*/
101-
@ExceptionHandler(value = {ServiceUnavailableException.class, DataAccessException.class})
102+
@ExceptionHandler(value = {ServiceUnavailableException.class,
103+
DataAccessException.class, MongoException.class})
102104
public ResponseEntity<Object> handleServiceUnavailableException(Exception ex,
103105
WebRequest request) {
104106
logger.error(String.format("Service unavailable, response code: %s",

src/main/java/uk/gov/companieshouse/pscdataapi/service/CompanyPscService.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
import java.util.List;
77
import org.springframework.beans.factory.annotation.Autowired;
88
import org.springframework.stereotype.Service;
9+
import org.springframework.transaction.annotation.Transactional;
10+
import uk.gov.companieshouse.api.InternalApiClient;
911
import uk.gov.companieshouse.api.psc.FullRecordCompanyPSCApi;
1012
import uk.gov.companieshouse.logging.Logger;
13+
import uk.gov.companieshouse.pscdataapi.api.ChsKafkaApiService;
1114
import uk.gov.companieshouse.pscdataapi.exceptions.BadRequestException;
1215
import uk.gov.companieshouse.pscdataapi.models.Created;
1316
import uk.gov.companieshouse.pscdataapi.models.PscDocument;
@@ -26,19 +29,28 @@ public class CompanyPscService {
2629
private CompanyPscTransformer transformer;
2730
@Autowired
2831
private CompanyPscRepository repository;
32+
@Autowired
33+
ChsKafkaApiService chsKafkaApiService;
34+
@Autowired
35+
InternalApiClient internalApiClient;
2936

3037
/**
3138
* Save or update a natural disqualification.
3239
* @param contextId Id used for chsKafkaCall.
3340
* @param requestBody Data to be saved.
3441
*/
42+
@Transactional
3543
public void insertPscRecord(String contextId, FullRecordCompanyPSCApi requestBody) {
3644
String notificationId = requestBody.getExternalData().getNotificationId();
3745
boolean isLatestRecord = isLatestRecord(notificationId, requestBody
3846
.getInternalData().getDeltaAt());
3947
if (isLatestRecord) {
4048
PscDocument document = transformer.transformPsc(notificationId, requestBody);
4149
save(contextId, notificationId, document);
50+
chsKafkaApiService.invokeChsKafkaApi(contextId,
51+
requestBody.getExternalData().getCompanyNumber(),
52+
notificationId, requestBody.getExternalData().getData().getKind());
53+
4254
} else {
4355
logger.info("PSC not persisted as the record provided is not the latest record.");
4456
}

src/main/java/uk/gov/companieshouse/pscdataapi/util/PscTransformationHelper.java

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
package uk.gov.companieshouse.pscdataapi.util;
22

3-
import java.time.LocalDateTime;
43
import uk.gov.companieshouse.api.psc.FullRecordCompanyPSCApi;
54
import uk.gov.companieshouse.api.psc.ItemLinkTypes;
6-
import uk.gov.companieshouse.pscdataapi.models.Created;
75
import uk.gov.companieshouse.pscdataapi.models.Links;
8-
import uk.gov.companieshouse.pscdataapi.models.NameElements;
9-
import uk.gov.companieshouse.pscdataapi.models.PscDocument;
10-
import uk.gov.companieshouse.pscdataapi.models.Updated;
116

127

138
public class PscTransformationHelper {
@@ -29,4 +24,37 @@ public static Links createLinks(FullRecordCompanyPSCApi requestBody) {
2924
links.setStatements(itemLinkTypes.getStatements());
3025
return links;
3126
}
27+
28+
/**
29+
* Maps kind from FullRecordCompanyPSCApi object to a valid resource kind for Chs kafka api.
30+
* @param kind psc kind
31+
* @return String containing valid resource kind
32+
*/
33+
public static String mapResourceKind(String kind) {
34+
String validResourceKind = new String();
35+
36+
switch (kind) {
37+
case "individual-person-with-significant-control":
38+
validResourceKind = "company-psc-individual";
39+
break;
40+
case "corporate-entity-person-with-significant-control":
41+
validResourceKind = "company-psc-corporate";
42+
break;
43+
case "legal-person-person-with-significant-control":
44+
validResourceKind = "company-psc-legal";
45+
break;
46+
case "super-secure-person-with-significant-control":
47+
validResourceKind = "company-psc-supersecure";
48+
break;
49+
case "individual-beneficial-owner":
50+
case "corporate-entity-beneficial-owner":
51+
case "legal-person-beneficial-owner":
52+
case "super-secure-beneficial-owner":
53+
validResourceKind = kind;
54+
break;
55+
default:
56+
}
57+
58+
return validResourceKind;
59+
}
3260
}

src/main/resources/application.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ management:
55
path-mapping:
66
health: /healthcheck
77

8+
chs:
9+
api:
10+
kafka:
11+
url: ${CHS_KAFKA_API_URL:localhost}
12+
resource-changed:
13+
uri: ${PSC_API_RESOURCE_CHANGED_URI:/private/resource-changed}
14+
815
logger:
916
namespace: psc-data-api
1017

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package uk.gov.companieshouse.pscdataapi.api;
2+
3+
import com.google.api.client.http.HttpHeaders;
4+
import com.google.api.client.http.HttpResponseException;
5+
import org.assertj.core.api.Assertions;
6+
import org.junit.Assert;
7+
import org.junit.jupiter.api.BeforeEach;
8+
import org.junit.jupiter.api.Test;
9+
import org.junit.jupiter.api.extension.ExtendWith;
10+
import org.mockito.ArgumentCaptor;
11+
import org.mockito.Captor;
12+
import org.mockito.InjectMocks;
13+
import org.mockito.Mock;
14+
import org.mockito.Mockito;
15+
import org.mockito.junit.jupiter.MockitoExtension;
16+
import uk.gov.companieshouse.api.InternalApiClient;
17+
import uk.gov.companieshouse.api.chskafka.ChangedResource;
18+
import uk.gov.companieshouse.api.error.ApiErrorResponseException;
19+
import uk.gov.companieshouse.api.handler.chskafka.PrivateChangedResourceHandler;
20+
import uk.gov.companieshouse.api.handler.chskafka.request.PrivateChangedResourcePost;
21+
import uk.gov.companieshouse.api.model.ApiResponse;
22+
import uk.gov.companieshouse.logging.Logger;
23+
import uk.gov.companieshouse.pscdataapi.exceptions.ServiceUnavailableException;
24+
import uk.gov.companieshouse.pscdataapi.util.TestHelper;
25+
26+
import static org.mockito.Mockito.times;
27+
import static org.mockito.Mockito.verify;
28+
import static org.mockito.Mockito.when;
29+
30+
@ExtendWith(MockitoExtension.class)
31+
public class ChsKafkaApiServiceTest {
32+
33+
@Mock
34+
private Logger logger;
35+
@Mock
36+
InternalApiClient internalApiClient;
37+
@Mock
38+
PrivateChangedResourceHandler privateChangedResourceHandler;
39+
@Mock
40+
private PrivateChangedResourcePost privateChangedResourcePost;
41+
@Mock
42+
private ApiResponse<Void> response;
43+
44+
private TestHelper testHelper;
45+
@InjectMocks
46+
private ChsKafkaApiService chsKafkaApiService;
47+
48+
@Captor
49+
ArgumentCaptor<ChangedResource> changedResourceCaptor;
50+
51+
@BeforeEach
52+
void setUp() {
53+
testHelper = new TestHelper();
54+
}
55+
56+
@Test
57+
void invokeChsKafkaEndpoint() throws ApiErrorResponseException {
58+
when(internalApiClient.privateChangedResourceHandler()).thenReturn(privateChangedResourceHandler);
59+
when(privateChangedResourceHandler.postChangedResource(Mockito.any(), Mockito.any())).thenReturn(privateChangedResourcePost);
60+
when(privateChangedResourcePost.execute()).thenReturn(response);
61+
62+
ApiResponse<?> apiResponse = chsKafkaApiService.invokeChsKafkaApi(
63+
TestHelper.X_REQUEST_ID, TestHelper.COMPANY_NUMBER, TestHelper.NOTIFICATION_ID, "kind");
64+
Assertions.assertThat(apiResponse).isNotNull();
65+
66+
verify(internalApiClient, times(1)).privateChangedResourceHandler();
67+
verify(privateChangedResourceHandler, times(1)).postChangedResource(Mockito.any(), changedResourceCaptor.capture());
68+
verify(privateChangedResourcePost, times(1)).execute();
69+
Assertions.assertThat(changedResourceCaptor.getValue().getEvent().getType()).isEqualTo("changed");
70+
}
71+
72+
@Test
73+
void invokeChsKafkaEndpointThrowsApiErrorException() throws ApiErrorResponseException {
74+
ApiErrorResponseException exception = new ApiErrorResponseException(new HttpResponseException.Builder(408, "Test Request timeout", new HttpHeaders()));
75+
when(internalApiClient.privateChangedResourceHandler()).thenReturn(privateChangedResourceHandler);
76+
when(privateChangedResourceHandler.postChangedResource(Mockito.any(), Mockito.any())).thenReturn(privateChangedResourcePost);
77+
when(privateChangedResourcePost.execute()).thenThrow(exception);
78+
79+
Assert.assertThrows(ServiceUnavailableException.class, () -> chsKafkaApiService.invokeChsKafkaApi(
80+
TestHelper.X_REQUEST_ID, TestHelper.COMPANY_NUMBER, TestHelper.NOTIFICATION_ID, "kind"));
81+
82+
verify(internalApiClient, times(1)).privateChangedResourceHandler();
83+
verify(privateChangedResourceHandler, times(1)).postChangedResource(Mockito.any(), changedResourceCaptor.capture());
84+
verify(privateChangedResourcePost, times(1)).execute();
85+
Assertions.assertThat(changedResourceCaptor.getValue().getEvent().getType()).isEqualTo("changed");
86+
}
87+
88+
@Test
89+
void invokeChsKafkaEndpointThrowsRuntimeException() throws ApiErrorResponseException {
90+
RuntimeException exception = new RuntimeException("Test Runtime exception");
91+
when(internalApiClient.privateChangedResourceHandler()).thenReturn(privateChangedResourceHandler);
92+
when(privateChangedResourceHandler.postChangedResource(Mockito.any(), Mockito.any())).thenReturn(privateChangedResourcePost);
93+
when(privateChangedResourcePost.execute()).thenThrow(exception);
94+
95+
Assert.assertThrows(RuntimeException.class, () -> chsKafkaApiService.invokeChsKafkaApi(
96+
TestHelper.X_REQUEST_ID, TestHelper.COMPANY_NUMBER, TestHelper.NOTIFICATION_ID, "kind"));
97+
98+
verify(internalApiClient, times(1)).privateChangedResourceHandler();
99+
verify(privateChangedResourceHandler, times(1)).postChangedResource(Mockito.any(), changedResourceCaptor.capture());
100+
verify(privateChangedResourcePost, times(1)).execute();
101+
Assertions.assertThat(changedResourceCaptor.getValue().getEvent().getType()).isEqualTo("changed");
102+
}
103+
}

src/test/java/uk/gov/companieshouse/pscdataapi/config/ApplicationConfigTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import org.junit.jupiter.api.BeforeEach;
55
import org.junit.jupiter.api.Test;
66
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
7+
import uk.gov.companieshouse.api.ApiClient;
8+
import uk.gov.companieshouse.api.InternalApiClient;
9+
710
import static org.hamcrest.core.IsNot.not;
811
import static org.hamcrest.MatcherAssert.assertThat;
912
import static org.hamcrest.Matchers.is;
@@ -23,6 +26,12 @@ void mongoCustomConversions() {
2326
assertThat(applicationConfig.mongoCustomConversions(), isA(MongoCustomConversions.class));
2427
}
2528

29+
@Test
30+
void internalApiClient() {
31+
assertThat(applicationConfig.internalApiClient(), is(not(nullValue())));
32+
assertThat(applicationConfig.internalApiClient(), isA(InternalApiClient.class));
33+
}
34+
2635
@Test
2736
void offsetDateTimeGenerator() {
2837
assertThat(applicationConfig.offsetDateTimeGenerator(), is(not(nullValue())));

src/test/java/uk/gov/companieshouse/pscdataapi/service/CompanyPscServiceTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
import org.mockito.InjectMocks;
1515
import org.mockito.Mock;
1616
import org.mockito.junit.jupiter.MockitoExtension;
17+
import uk.gov.companieshouse.api.psc.Data;
1718
import uk.gov.companieshouse.api.psc.ExternalData;
1819
import uk.gov.companieshouse.api.psc.FullRecordCompanyPSCApi;
1920
import uk.gov.companieshouse.api.psc.InternalData;
2021
import uk.gov.companieshouse.logging.Logger;
22+
import uk.gov.companieshouse.pscdataapi.api.ChsKafkaApiService;
2123
import uk.gov.companieshouse.pscdataapi.models.PscDocument;
2224
import uk.gov.companieshouse.pscdataapi.models.Updated;
2325
import uk.gov.companieshouse.pscdataapi.repository.CompanyPscRepository;
@@ -43,6 +45,8 @@ class CompanyPscServiceTest {
4345

4446
@Mock
4547
private CompanyPscTransformer transformer;
48+
@Mock
49+
private ChsKafkaApiService chsKafkaApiService;
4650

4751
@Captor
4852
private ArgumentCaptor<String> dateCaptor;
@@ -60,7 +64,10 @@ public void setUp() {
6064
request = new FullRecordCompanyPSCApi();
6165
InternalData internal = new InternalData();
6266
ExternalData external = new ExternalData();
67+
Data data = new Data();
6368
external.setNotificationId(PSC_ID);
69+
external.setData(data);
70+
data.setKind("kind");
6471
internal.setDeltaAt(date);
6572
request.setInternalData(internal);
6673
request.setExternalData(external);

0 commit comments

Comments
 (0)