diff --git a/pom.xml b/pom.xml index 85d61a5a..88f46ef6 100644 --- a/pom.xml +++ b/pom.xml @@ -31,8 +31,9 @@ 2.0.326 1.0.4 0.4.1 - 1.0.4 + 1.0.8 1.4.5 + 1.5.17 3.0.0-M5 @@ -171,6 +172,16 @@ kafka-models ${kafka-models.version} + + org.springframework.boot + spring-boot-starter-aop + + + org.yaml + snakeyaml + + + @@ -219,8 +230,9 @@ ${skip.unit.tests} key - g9yZIA81Zo9J46Kzp3JPbfld6kOqxR47EAYqXbRV + key key + document_api_local_url diff --git a/src/main/java/uk/gov/companieshouse/pscdataapi/api/ChsKafkaApiService.java b/src/main/java/uk/gov/companieshouse/pscdataapi/api/ChsKafkaApiService.java index a34a9639..7a028d22 100644 --- a/src/main/java/uk/gov/companieshouse/pscdataapi/api/ChsKafkaApiService.java +++ b/src/main/java/uk/gov/companieshouse/pscdataapi/api/ChsKafkaApiService.java @@ -37,6 +37,8 @@ public class ChsKafkaApiService { * @param notificationId mongo id * @return passes request to api response handling */ + + @StreamEvents public ApiResponse invokeChsKafkaApi(String contextId, String companyNumber, String notificationId, String kind) { internalApiClient.setBasePath(chsKafkaApiUrl); diff --git a/src/main/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspect.java b/src/main/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspect.java new file mode 100644 index 00000000..2b2e259c --- /dev/null +++ b/src/main/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspect.java @@ -0,0 +1,41 @@ +package uk.gov.companieshouse.pscdataapi.api; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; +import uk.gov.companieshouse.logging.Logger; +import uk.gov.companieshouse.pscdataapi.config.FeatureFlags; + +@Aspect +@Component +@ConditionalOnProperty(prefix = "feature", name = "seeding_collection_enabled") +public class ResourceChangedApiServiceAspect { + + private final FeatureFlags featureFlags; + private final Logger logger; + + public ResourceChangedApiServiceAspect(FeatureFlags featureFlags, Logger logger) { + this.featureFlags = featureFlags; + this.logger = logger; + } + + /** + * Feature flag check. + * @param proceedingJoinPoint the proceeding join point. + * @return returns an object. + * @throws Throwable throws something. + */ + @Around("@annotation(StreamEvents)") + public Object invokeChsKafkaApi(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { + + if (featureFlags.isStreamHookDisabled()) { + logger.debug("Stream hook disabled; not publishing change to chs-kafka-api"); + return null; + } else { + logger.debug("Stream hook enabled; publishing change to chs-kafka-api"); + return proceedingJoinPoint.proceed(); + } + } +} \ No newline at end of file diff --git a/src/main/java/uk/gov/companieshouse/pscdataapi/api/StreamEvents.java b/src/main/java/uk/gov/companieshouse/pscdataapi/api/StreamEvents.java new file mode 100644 index 00000000..5edf922d --- /dev/null +++ b/src/main/java/uk/gov/companieshouse/pscdataapi/api/StreamEvents.java @@ -0,0 +1,11 @@ +package uk.gov.companieshouse.pscdataapi.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface StreamEvents { +} diff --git a/src/main/java/uk/gov/companieshouse/pscdataapi/config/FeatureFlags.java b/src/main/java/uk/gov/companieshouse/pscdataapi/config/FeatureFlags.java new file mode 100644 index 00000000..47920ef7 --- /dev/null +++ b/src/main/java/uk/gov/companieshouse/pscdataapi/config/FeatureFlags.java @@ -0,0 +1,19 @@ +package uk.gov.companieshouse.pscdataapi.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class FeatureFlags { + + private final boolean streamHookDisabled; + + public FeatureFlags( + @Value("${feature.seeding_collection_enabled}") boolean streamHookDisabled) { + this.streamHookDisabled = streamHookDisabled; + } + + public boolean isStreamHookDisabled() { + return streamHookDisabled; + } +} diff --git a/src/main/java/uk/gov/companieshouse/pscdataapi/models/PscDocument.java b/src/main/java/uk/gov/companieshouse/pscdataapi/models/PscDocument.java index 84fb33eb..775b0b1a 100644 --- a/src/main/java/uk/gov/companieshouse/pscdataapi/models/PscDocument.java +++ b/src/main/java/uk/gov/companieshouse/pscdataapi/models/PscDocument.java @@ -5,9 +5,6 @@ import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; -import uk.gov.companieshouse.api.psc.Identification; - - @JsonInclude(JsonInclude.Include.NON_NULL) @Document(collection = "delta_company_pscs") diff --git a/src/main/java/uk/gov/companieshouse/pscdataapi/repository/CompanyPscRepository.java b/src/main/java/uk/gov/companieshouse/pscdataapi/repository/CompanyPscRepository.java index 31cc2723..0a562151 100644 --- a/src/main/java/uk/gov/companieshouse/pscdataapi/repository/CompanyPscRepository.java +++ b/src/main/java/uk/gov/companieshouse/pscdataapi/repository/CompanyPscRepository.java @@ -7,7 +7,6 @@ import org.springframework.data.mongodb.repository.Aggregation; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.Query; -import uk.gov.companieshouse.api.model.PscStatementDocument; import uk.gov.companieshouse.pscdataapi.models.PscDocument; public interface CompanyPscRepository extends MongoRepository { diff --git a/src/main/java/uk/gov/companieshouse/pscdataapi/service/CompanyPscService.java b/src/main/java/uk/gov/companieshouse/pscdataapi/service/CompanyPscService.java index 7299db1e..ef251ab0 100644 --- a/src/main/java/uk/gov/companieshouse/pscdataapi/service/CompanyPscService.java +++ b/src/main/java/uk/gov/companieshouse/pscdataapi/service/CompanyPscService.java @@ -43,7 +43,6 @@ import uk.gov.companieshouse.pscdataapi.repository.CompanyPscRepository; import uk.gov.companieshouse.pscdataapi.transform.CompanyPscTransformer; - @Service public class CompanyPscService { diff --git a/src/main/java/uk/gov/companieshouse/pscdataapi/transform/CompanyPscTransformer.java b/src/main/java/uk/gov/companieshouse/pscdataapi/transform/CompanyPscTransformer.java index d9f83553..cbf7a538 100644 --- a/src/main/java/uk/gov/companieshouse/pscdataapi/transform/CompanyPscTransformer.java +++ b/src/main/java/uk/gov/companieshouse/pscdataapi/transform/CompanyPscTransformer.java @@ -34,7 +34,6 @@ import uk.gov.companieshouse.pscdataapi.models.Updated; import uk.gov.companieshouse.pscdataapi.util.PscTransformationHelper; - @Component public class CompanyPscTransformer { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 199a8b36..f4f373c2 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,4 +1,5 @@ management.endpoints.web.base-path=/psc-data-api management.endpoints.web.path-mapping.health=/healthcheck chs.api.kafka.uri=/private/resource-changed -chs.api.kafka.kind=someKind \ No newline at end of file +chs.api.kafka.kind=someKind +feature.seeding_collection_enabled=${SEEDING_COLLECTION_ENABLED:false} diff --git a/src/test/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspectFeatureFlagDisabledITest.java b/src/test/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspectFeatureFlagDisabledITest.java new file mode 100644 index 00000000..effcf5f6 --- /dev/null +++ b/src/test/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspectFeatureFlagDisabledITest.java @@ -0,0 +1,79 @@ +package uk.gov.companieshouse.pscdataapi.api; + +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.*; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import uk.gov.companieshouse.api.InternalApiClient; +import uk.gov.companieshouse.api.chskafka.ChangedResource; +import uk.gov.companieshouse.api.error.ApiErrorResponseException; +import uk.gov.companieshouse.api.handler.chskafka.PrivateChangedResourceHandler; +import uk.gov.companieshouse.api.handler.chskafka.request.PrivateChangedResourcePost; +import uk.gov.companieshouse.api.http.HttpClient; +import uk.gov.companieshouse.api.model.ApiResponse; +import uk.gov.companieshouse.api.sdk.ApiClientService; +import uk.gov.companieshouse.pscdataapi.util.TestHelper; + +@SpringBootTest +class ResourceChangedApiServiceAspectFeatureFlagDisabledITest { + + @InjectMocks + private ChsKafkaApiService chsKafkaApiService; + + @MockBean + private ApiClientService apiClientService; + + @Mock + private InternalApiClient internalApiClient; + + @Mock + private ChangedResource changedResource; + @Mock + private PrivateChangedResourceHandler privateChangedResourceHandler; + @Mock + private PrivateChangedResourcePost changedResourcePost; + @Mock + private ApiResponse response; + @Mock + private HttpClient httpClient; + + @MockBean + private ChsKafkaApiService mapper; + + private TestHelper testHelper; + + @Captor + ArgumentCaptor changedResourceCaptor; + + + + @BeforeEach + void setup() { + when(internalApiClient.getHttpClient()).thenReturn(httpClient); + testHelper = new TestHelper(); + } + + @Test + void testThatKafkaApiShouldBeCalledWhenFeatureFlagDisabled() + throws ApiErrorResponseException { + + when(internalApiClient.privateChangedResourceHandler()).thenReturn( + privateChangedResourceHandler); + when(privateChangedResourceHandler.postChangedResource(Mockito.any(), Mockito.any())).thenReturn( + changedResourcePost); + when(changedResourcePost.execute()).thenReturn(response); + + ApiResponse apiResponse = chsKafkaApiService.invokeChsKafkaApi(TestHelper.X_REQUEST_ID, TestHelper.COMPANY_NUMBER, TestHelper.NOTIFICATION_ID, "kind"); + + Assertions.assertThat(apiResponse).isNotNull(); + + verify(internalApiClient).privateChangedResourceHandler(); + verify(privateChangedResourceHandler,times(1)).postChangedResource(Mockito.any(), changedResourceCaptor.capture()); + verify(changedResourcePost, times(1)).execute(); + } +} diff --git a/src/test/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspectFeatureFlagEnabledITest.java b/src/test/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspectFeatureFlagEnabledITest.java new file mode 100644 index 00000000..ed04b8eb --- /dev/null +++ b/src/test/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspectFeatureFlagEnabledITest.java @@ -0,0 +1,77 @@ +package uk.gov.companieshouse.pscdataapi.api; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ActiveProfiles; +import uk.gov.companieshouse.api.InternalApiClient; +import uk.gov.companieshouse.api.chskafka.ChangedResource; +import uk.gov.companieshouse.api.error.ApiErrorResponseException; +import uk.gov.companieshouse.api.handler.chskafka.PrivateChangedResourceHandler; +import uk.gov.companieshouse.api.handler.chskafka.request.PrivateChangedResourcePost; +import uk.gov.companieshouse.api.http.HttpClient; +import uk.gov.companieshouse.api.model.ApiResponse; +import uk.gov.companieshouse.api.request.RequestExecutor; +import uk.gov.companieshouse.api.sdk.ApiClientService; +import uk.gov.companieshouse.pscdataapi.exceptions.ServiceUnavailableException; +import uk.gov.companieshouse.pscdataapi.util.TestHelper; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +@SpringBootTest +@ActiveProfiles("feature_flag_enabled") +class ResourceChangedApiServiceAspectFeatureFlagEnabledITest { + @Autowired + private ChsKafkaApiService chsKafkaApiService; + + @MockBean + private ApiClientService apiClientService; + + @Mock + private InternalApiClient internalApiClient; + @Mock + private PrivateChangedResourceHandler privateChangedResourceHandler; + @Mock + private PrivateChangedResourcePost changedResourcePost; + @Mock + private ApiResponse response; + @Mock + private HttpClient httpClient; + + @Mock + private ChangedResource changedResource; + + private String url = "testurl"; + @Mock + private RequestExecutor requestExecutor; + private TestHelper testHelper; + + @BeforeEach + void setup() { + + testHelper = new TestHelper(); + } + @Test + void testThatAspectShouldNotProceedWhenFeatureFlagEnabled() throws ServiceUnavailableException, ApiErrorResponseException { + + when(internalApiClient.privateChangedResourceHandler()).thenReturn( + privateChangedResourceHandler); + when(privateChangedResourceHandler.postChangedResource(any(), any())).thenReturn( + changedResourcePost); + when(changedResourcePost.execute()).thenReturn(response); + + chsKafkaApiService.invokeChsKafkaApi(TestHelper.X_REQUEST_ID, TestHelper.COMPANY_NUMBER, TestHelper.NOTIFICATION_ID, "kind"); + + verifyNoInteractions(apiClientService); + verifyNoInteractions(internalApiClient); + verifyNoInteractions(privateChangedResourceHandler); + verifyNoInteractions(changedResourcePost); + } +} diff --git a/src/test/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspectTest.java b/src/test/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspectTest.java new file mode 100644 index 00000000..5fcdce4a --- /dev/null +++ b/src/test/java/uk/gov/companieshouse/pscdataapi/api/ResourceChangedApiServiceAspectTest.java @@ -0,0 +1,55 @@ +package uk.gov.companieshouse.pscdataapi.api; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.companieshouse.logging.Logger; +import uk.gov.companieshouse.pscdataapi.config.FeatureFlags; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.*; + + +@ExtendWith(MockitoExtension.class) +class ResourceChangedApiServiceAspectTest { + + @InjectMocks + private ResourceChangedApiServiceAspect resourceChangedApiServiceAspect; + @Mock + private ProceedingJoinPoint proceedingJoinPoint; + @Mock + private Object object; + @Mock + private Logger logger; + @Mock + private FeatureFlags featureFlags; + + @Test + void testAspectDoesNotProceedWhenFlagEnabled() throws Throwable { + //given + when(featureFlags.isStreamHookDisabled()).thenReturn(true); + // when + Object actual = resourceChangedApiServiceAspect.invokeChsKafkaApi(proceedingJoinPoint); + + // then + assertNull(actual); + verifyNoInteractions(proceedingJoinPoint); + verify(logger).debug("Stream hook disabled; not publishing change to chs-kafka-api"); + } + + @Test + void testAspectProceedsWhenFlagDisabled() throws Throwable { + //given + when(proceedingJoinPoint.proceed()).thenReturn(object); + //when + Object actual = resourceChangedApiServiceAspect.invokeChsKafkaApi(proceedingJoinPoint); + //then + assertSame(object, actual); + verify(proceedingJoinPoint).proceed(); + verify(logger).debug("Stream hook enabled; publishing change to chs-kafka-api"); + } +} \ No newline at end of file diff --git a/src/test/java/uk/gov/companieshouse/pscdataapi/controller/CompanyPscControllerTest.java b/src/test/java/uk/gov/companieshouse/pscdataapi/controller/CompanyPscControllerTest.java index 7eb4639b..872788aa 100644 --- a/src/test/java/uk/gov/companieshouse/pscdataapi/controller/CompanyPscControllerTest.java +++ b/src/test/java/uk/gov/companieshouse/pscdataapi/controller/CompanyPscControllerTest.java @@ -12,9 +12,14 @@ import uk.gov.companieshouse.api.exception.ResourceNotFoundException; import uk.gov.companieshouse.api.exception.ServiceUnavailableException; import uk.gov.companieshouse.api.psc.*; +import uk.gov.companieshouse.pscdataapi.models.PscDocument; +import uk.gov.companieshouse.pscdataapi.models.Updated; import uk.gov.companieshouse.pscdataapi.service.CompanyPscService; import uk.gov.companieshouse.pscdataapi.util.TestHelper; +import java.time.LocalDate; +import java.time.OffsetDateTime; + import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import static org.springframework.http.MediaType.APPLICATION_JSON; @@ -38,6 +43,18 @@ class CompanyPscControllerTest { private static final String ERIC_PRIVILEGES = "*"; private static final String ERIC_AUTH = "internal-app"; + private FullRecordCompanyPSCApi request; + private PscDocument document; + private SuperSecure superSecure; + private SuperSecureBeneficialOwner superSecureBeneficialOwner; + private Individual individual; + private IndividualBeneficialOwner individualBeneficialOwner; + private CorporateEntity corporateEntity; + private CorporateEntityBeneficialOwner corporateEntityBeneficialOwner; + private LegalPerson legalPerson; + private LegalPersonBeneficialOwner legalPersonBeneficialOwner; + private TestHelper testHelper; + private static final String PUT_URL = String.format( "/company/%s/persons-with-significant-control/%s/full_record", MOCK_COMPANY_NUMBER, MOCK_NOTIFICATION_ID); private static final String GET_Individual_URL = String.format( diff --git a/src/test/java/uk/gov/companieshouse/pscdataapi/util/TestHelper.java b/src/test/java/uk/gov/companieshouse/pscdataapi/util/TestHelper.java index b8932ffe..50b00fef 100644 --- a/src/test/java/uk/gov/companieshouse/pscdataapi/util/TestHelper.java +++ b/src/test/java/uk/gov/companieshouse/pscdataapi/util/TestHelper.java @@ -31,7 +31,7 @@ public class TestHelper { public static final String PSC_ID = "pscId"; public static final String X_REQUEST_ID = "654321"; - private TestHelper(){} + public TestHelper(){} public static FullRecordCompanyPSCApi buildFullRecordPsc(String kind) { return buildFullRecordPsc(kind, false); diff --git a/src/test/resources/application-feature_flag_enabled.properties b/src/test/resources/application-feature_flag_enabled.properties new file mode 100644 index 00000000..7dde4496 --- /dev/null +++ b/src/test/resources/application-feature_flag_enabled.properties @@ -0,0 +1,22 @@ +company-metrics-api.endpoint=localhost + +chs.kafka.api.endpoint=http://localhost:8888 +chs.kafka.api.key=chsApiKey + +api.api-url=${API_URL:localhost} +api.api-key=${CHS_API_KEY:chsApiKey} + +feature.seeding_collection_enabled=true + +items-per-page-max-internal=500 + +management.endpoints.web.base-path=/psc-data-api +management.endpoints.web.path-mapping.health=/healthcheck +chs.api.kafka.url=${CHS_KAFKA_API_URL:localhost} +chs.api.kafka.resource-changed.uri=${PSC_API_RESOURCE_CHANGED_URI:/private/resource-changed} +chs.api.metrics.url=${API_LOCAL_URL:localhost} +logger.namespace=psc-data-api +spring.data.mongodb.uri=${MONGODB_URL:mongodb://mongo:27017} +spring.data.mongodb.name=company_pscs +spring.jackson.default-property-inclusion=NON_NULL +mongodb.pscs.collection.name=delta_company_pscs \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 00000000..d2071a5e --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,22 @@ +company-metrics-api.endpoint=localhost + +chs.kafka.api.endpoint=http://localhost:8888 +chs.kafka.api.key=chsApiKey + +api.api-url=${API_URL:localhost} +api.api-key=${CHS_API_KEY:chsApiKey} + +feature.seeding_collection_enabled=false + +items-per-page-max-internal=500 + +management.endpoints.web.base-path=/psc-data-api +management.endpoints.web.path-mapping.health=/healthcheck +chs.api.kafka.url=${CHS_KAFKA_API_URL:localhost} +chs.api.kafka.resource-changed.uri=${PSC_API_RESOURCE_CHANGED_URI:/private/resource-changed} +chs.api.metrics.url=${API_LOCAL_URL:localhost} +logger.namespace=psc-data-api +spring.data.mongodb.uri=${MONGODB_URL:mongodb://mongo:27017} +spring.data.mongodb.name=company_pscs +spring.jackson.default-property-inclusion=NON_NULL +mongodb.pscs.collection.name=delta_company_pscs \ No newline at end of file