diff --git a/src/main/java/gov/cabinetoffice/gap/adminbackend/config/OpenSearchConfig.java b/src/main/java/gov/cabinetoffice/gap/adminbackend/config/OpenSearchSqsProperties.java similarity index 61% rename from src/main/java/gov/cabinetoffice/gap/adminbackend/config/OpenSearchConfig.java rename to src/main/java/gov/cabinetoffice/gap/adminbackend/config/OpenSearchSqsProperties.java index 82f5df8e..7c2b59be 100644 --- a/src/main/java/gov/cabinetoffice/gap/adminbackend/config/OpenSearchConfig.java +++ b/src/main/java/gov/cabinetoffice/gap/adminbackend/config/OpenSearchSqsProperties.java @@ -11,11 +11,8 @@ @Builder @AllArgsConstructor @NoArgsConstructor -@Configuration -@ConfigurationProperties(prefix = "open-search") -public class OpenSearchConfig { - private String url; - private String domain; - private String username; - private String password; +@Configuration("openSearchSqsProperties") +@ConfigurationProperties(prefix = "open-search-sqs") +public class OpenSearchSqsProperties { + private String queueUrl; } diff --git a/src/main/java/gov/cabinetoffice/gap/adminbackend/config/WebClientConfig.java b/src/main/java/gov/cabinetoffice/gap/adminbackend/config/WebClientConfig.java deleted file mode 100644 index b1882ff4..00000000 --- a/src/main/java/gov/cabinetoffice/gap/adminbackend/config/WebClientConfig.java +++ /dev/null @@ -1,25 +0,0 @@ -package gov.cabinetoffice.gap.adminbackend.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.reactive.ReactorClientHttpConnector; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.netty.http.client.HttpClient; -import reactor.netty.resources.ConnectionProvider; - -import java.time.Duration; - -@Configuration -public class WebClientConfig { - - @Bean - public WebClient getWebClient() { - final ConnectionProvider provider = ConnectionProvider.builder("fixed") - .maxIdleTime(Duration.ofSeconds(30)) - .build(); - - return WebClient.builder() - .clientConnector(new ReactorClientHttpConnector(HttpClient.create(provider))) - .build(); - } -} diff --git a/src/main/java/gov/cabinetoffice/gap/adminbackend/dtos/SendAdvertToSqsDto.java b/src/main/java/gov/cabinetoffice/gap/adminbackend/dtos/SendAdvertToSqsDto.java new file mode 100644 index 00000000..991aa945 --- /dev/null +++ b/src/main/java/gov/cabinetoffice/gap/adminbackend/dtos/SendAdvertToSqsDto.java @@ -0,0 +1,4 @@ +package gov.cabinetoffice.gap.adminbackend.dtos; + +public record SendAdvertToSqsDto(String contentfulEntryId, String type) { +} diff --git a/src/main/java/gov/cabinetoffice/gap/adminbackend/services/GrantAdvertService.java b/src/main/java/gov/cabinetoffice/gap/adminbackend/services/GrantAdvertService.java index 9eeece9f..7f3e4a53 100644 --- a/src/main/java/gov/cabinetoffice/gap/adminbackend/services/GrantAdvertService.java +++ b/src/main/java/gov/cabinetoffice/gap/adminbackend/services/GrantAdvertService.java @@ -1,5 +1,7 @@ package gov.cabinetoffice.gap.adminbackend.services; +import com.amazonaws.services.sqs.AmazonSQS; +import com.amazonaws.services.sqs.model.SendMessageRequest; import com.contentful.java.cda.CDAArray; import com.contentful.java.cda.CDAClient; import com.contentful.java.cda.CDAEntry; @@ -7,8 +9,11 @@ import com.contentful.java.cma.CMAClient; import com.contentful.java.cma.model.CMAEntry; import com.contentful.java.cma.model.rich.CMARichDocument; +import com.fasterxml.jackson.databind.ObjectMapper; import gov.cabinetoffice.gap.adminbackend.config.ContentfulConfigProperties; import gov.cabinetoffice.gap.adminbackend.config.FeatureFlagsConfigurationProperties; +import gov.cabinetoffice.gap.adminbackend.config.OpenSearchSqsProperties; +import gov.cabinetoffice.gap.adminbackend.dtos.SendAdvertToSqsDto; import gov.cabinetoffice.gap.adminbackend.dtos.grantadvert.GetGrantAdvertPageResponseDTO; import gov.cabinetoffice.gap.adminbackend.dtos.grantadvert.GetGrantAdvertPublishingInformationResponseDTO; import gov.cabinetoffice.gap.adminbackend.dtos.grantadvert.GetGrantAdvertStatusResponseDTO; @@ -62,14 +67,15 @@ public class GrantAdvertService { private final CMAClient contentfulManagementClient; private final CDAClient contentfulDeliveryClient; private final UserService userService; - private final OpenSearchService openSearchService; private final WebClient.Builder webClientBuilder; - - private final WebClient webClient; + private final AmazonSQS amazonSqs; + private final ObjectMapper mapper; private final Clock clock; private final ContentfulConfigProperties contentfulProperties; private final FeatureFlagsConfigurationProperties featureFlagsProperties; + private final OpenSearchSqsProperties openSearchSqsProperties; + public GrantAdvert save(GrantAdvert advert) { final Authentication auth = SecurityContextHolder.getContext().getAuthentication(); Optional.ofNullable(auth) @@ -314,26 +320,29 @@ public GrantAdvert publishAdvert(UUID advertId) { } contentfulAdvert = contentfulManagementClient.entries().fetchOne(contentfulAdvert.getId()); + final boolean isPublished = Boolean.TRUE.equals(contentfulAdvert.isPublished()); advert.setStatus(GrantAdvertStatus.PUBLISHED); advert.setContentfulSlug(contentfulAdvert.getField("label", CONTENTFUL_LOCALE)); advert.setContentfulEntryId(contentfulAdvert.getId()); - if (Boolean.FALSE.equals(contentfulAdvert.isPublished())) { - final CMAEntry publishedAdvert = contentfulManagementClient.entries().publish(contentfulAdvert); - openSearchService.indexEntry(publishedAdvert); + if (!isPublished) { + contentfulManagementClient.entries().publish(contentfulAdvert); } updateGrantAdvertApplicationDates(advert); - return save(advert); + final GrantAdvert savedAdvert = save(advert); + sendMessageToQueue(new SendAdvertToSqsDto(contentfulAdvert.getId(), "ADD")); + + return savedAdvert; } public void unpublishAdvert(UUID advertId) { final GrantAdvert advert = this.getAdvertById(advertId); final CMAEntry contentfulAdvert = contentfulManagementClient.entries().fetchOne(advert.getContentfulEntryId()); + final boolean isPublished = Boolean.TRUE.equals(contentfulAdvert.isPublished()); - if (Boolean.TRUE.equals(contentfulAdvert.isPublished())) { - final CMAEntry unpublishedAd = contentfulManagementClient.entries().unPublish(contentfulAdvert); - openSearchService.removeIndexEntry(unpublishedAd); + if (isPublished) { + contentfulManagementClient.entries().unPublish(contentfulAdvert); } advert.setStatus(GrantAdvertStatus.DRAFT); @@ -341,6 +350,20 @@ public void unpublishAdvert(UUID advertId) { advert.setUnpublishedDate(Instant.now()); save(advert); + sendMessageToQueue(new SendAdvertToSqsDto(contentfulAdvert.getId(), "REMOVE")); + } + + public void sendMessageToQueue(final SendAdvertToSqsDto advertDto) { + final UUID messageId = UUID.randomUUID(); + final String messageBody = mapper.valueToTree(advertDto).toString(); + final SendMessageRequest messageRequest = new SendMessageRequest() + .withQueueUrl(openSearchSqsProperties.getQueueUrl()) + .withMessageGroupId(messageId.toString()) + .withMessageBody(messageBody) + .withMessageDeduplicationId(messageId.toString()); + + amazonSqs.sendMessage(messageRequest); + log.info("Message sent to queue for advert with contentful ID {}", advertDto.contentfulEntryId()); } private CMAEntry createAdvertInContentful(final GrantAdvert grantAdvert) { @@ -438,7 +461,8 @@ private void createRichTextQuestionsInContentful(final GrantAdvert advert, final } final Instant now = Instant.now(); - webClient.patch() + webClientBuilder.build() + .patch() .uri(contentfulUrl) .headers(h -> { h.set("Authorization", String.format("Bearer %s", contentfulProperties.getAccessToken())); diff --git a/src/main/java/gov/cabinetoffice/gap/adminbackend/services/OpenSearchService.java b/src/main/java/gov/cabinetoffice/gap/adminbackend/services/OpenSearchService.java deleted file mode 100644 index adff5511..00000000 --- a/src/main/java/gov/cabinetoffice/gap/adminbackend/services/OpenSearchService.java +++ /dev/null @@ -1,96 +0,0 @@ -package gov.cabinetoffice.gap.adminbackend.services; - -import com.contentful.java.cma.model.CMAEntry; -import gov.cabinetoffice.gap.adminbackend.config.ContentfulConfigProperties; -import gov.cabinetoffice.gap.adminbackend.config.OpenSearchConfig; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import static org.apache.http.HttpHeaders.AUTHORIZATION; -import static org.apache.http.HttpHeaders.CONTENT_TYPE; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Service; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Mono; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -@Service -@RequiredArgsConstructor -@Slf4j -public class OpenSearchService { - - private final WebClient webClient; - private final OpenSearchConfig openSearchConfig; - private final ContentfulConfigProperties contentfulProperties; - - public void indexEntry(final CMAEntry contentfulEntry) { - final String body = getContentfulAdvertAsJson(contentfulEntry.getId()); - webClient.put() - .uri(createUrl(contentfulEntry)) - .body(Mono.just(body), String.class) - .header(CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE + "; " + StandardCharsets.UTF_8.name()) - .header(AUTHORIZATION, createAuthHeader()) - .retrieve() - .bodyToMono(void.class) - .doOnError(e -> log.error("Failed to create an index entry for ad " + contentfulEntry.getId() + "in open search: {}", e.getMessage())) - .block(); - } - - public void removeIndexEntry(final CMAEntry contentfulEntry) { - final String body = getContentfulAdvertAsJson(contentfulEntry.getId()); - webClient.method(HttpMethod.DELETE) - .uri(createUrl(contentfulEntry)) - .body(Mono.just(body), String.class) - .header(CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE + "; " + StandardCharsets.UTF_8.name()) - .header(AUTHORIZATION, createAuthHeader()) - .retrieve() - .bodyToMono(void.class) - .doOnError(e -> log.error("Failed to delete an index entry for ad " + contentfulEntry.getId() + "in open search: {}", e.getMessage())) - .block(); - } - - private String createUrl(final CMAEntry contentfulEntry) { - return openSearchConfig.getUrl() + "/" + openSearchConfig.getDomain() + "/_doc/" + contentfulEntry.getId(); - } - - private String createAuthHeader() { - final String auth = openSearchConfig.getUsername() + ":" + openSearchConfig.getPassword(); - return "Basic " + Base64.getEncoder().encodeToString(auth.getBytes()); - } - - private String getContentfulAdvertAsJson(String entryId) { - final String contentfulUrl = String.format( - "https://api.contentful.com/spaces/%1$s/environments/%2$s/entries/%3$s", - contentfulProperties.getSpaceId(), - contentfulProperties.getEnvironmentId(), - entryId - ); - - return webClient.get() - .uri(contentfulUrl) - .headers(h -> - h.set("Authorization", String.format("Bearer %s", contentfulProperties.getAccessToken())) - ) - .retrieve() - .onStatus(HttpStatus::isError, response -> { - log.error("Contentful response -------------------"); - log.error(response.statusCode().toString()); - log.error(response.bodyToMono(String.class).toString()); - log.error("End Contentful response ---------------"); - - return Mono.empty(); - }) - .bodyToMono(String.class) - .doOnError(exception -> - log.error( - "getContentfulAdvertAsJson failed on GET to {}, with message: {}", - contentfulUrl, - exception - ) - ) - .block(); - } -} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4ae4e4fe..6d4948a2 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -78,7 +78,4 @@ aws.kms.stage=stage spring.jpa.properties.hibernate.order_by.default_null_ordering=last -open-search.url=https://search-url -open-search.domain=domain -open-search.username=username -open-search.password=password +open-search-sqs.queueUrl=an-sqs-url diff --git a/src/test/java/gov/cabinetoffice/gap/adminbackend/services/GrantAdvertServiceTest.java b/src/test/java/gov/cabinetoffice/gap/adminbackend/services/GrantAdvertServiceTest.java index 5f7728b8..a1e3e7c6 100644 --- a/src/test/java/gov/cabinetoffice/gap/adminbackend/services/GrantAdvertServiceTest.java +++ b/src/test/java/gov/cabinetoffice/gap/adminbackend/services/GrantAdvertServiceTest.java @@ -1,14 +1,18 @@ package gov.cabinetoffice.gap.adminbackend.services; +import com.amazonaws.services.sqs.AmazonSQS; import com.contentful.java.cda.CDAArray; import com.contentful.java.cda.CDAClient; import com.contentful.java.cda.FetchQuery; import com.contentful.java.cma.CMAClient; import com.contentful.java.cma.ModuleEntries; import com.contentful.java.cma.model.CMAEntry; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import gov.cabinetoffice.gap.adminbackend.annotations.WithAdminSession; import gov.cabinetoffice.gap.adminbackend.config.ContentfulConfigProperties; import gov.cabinetoffice.gap.adminbackend.config.FeatureFlagsConfigurationProperties; +import gov.cabinetoffice.gap.adminbackend.config.OpenSearchSqsProperties; import gov.cabinetoffice.gap.adminbackend.dtos.grantadvert.GetGrantAdvertPageResponseDTO; import gov.cabinetoffice.gap.adminbackend.dtos.grantadvert.GetGrantAdvertPublishingInformationResponseDTO; import gov.cabinetoffice.gap.adminbackend.dtos.grantadvert.GetGrantAdvertStatusResponseDTO; @@ -72,19 +76,30 @@ class GrantAdvertServiceTest { @Mock private CDAClient contentfulDeliveryClient; + @Mock + private ObjectMapper mapper; + + @Mock + private AmazonSQS amazonSqs; + @Spy private GrantAdvertMapper grantAdvertMapper = new GrantAdvertMapperImpl(); @Spy private ContentfulConfigProperties contentfulConfigProperties = ContentfulConfigProperties.builder() - .accessToken("an-access-token").environmentId("dev").spaceId("a-space-id") - .deliveryAPIAccessToken("a-delivery-access-token").build(); + .accessToken("an-access-token") + .environmentId("dev") + .spaceId("a-space-id") + .deliveryAPIAccessToken("a-delivery-access-token") + .build(); - @Mock - private ModuleEntries contentfulEntries; + @Spy + private OpenSearchSqsProperties openSearchSqsProperties = OpenSearchSqsProperties.builder() + .queueUrl("a-url") + .build(); @Mock - private ModuleEntries.Async async; + private ModuleEntries contentfulEntries; @Mock private FetchQuery mockedFetchQuery; @@ -104,12 +119,6 @@ class GrantAdvertServiceTest { @Mock private UserService userService; - @Mock - private OpenSearchService openSearchService; - - @Mock - private WebClient webClient; - @InjectMocks @Spy private GrantAdvertService grantAdvertService; @@ -850,14 +859,14 @@ void publishAdvert_successfullyPublishedAdvert() { when(contentfulEntries.fetchOne(contentfulAdvertId)).thenReturn(publishedContentfulAdvert); - //when(contentfulEntries.async()).thenReturn(async); - doReturn(mockGrantAdvert).when(grantAdvertService).save(any()); + final WebClient webClient = mock(WebClient.class); final WebClient.RequestHeadersSpec requestHeadersSpec = mock(WebClient.RequestHeadersSpec.class); final WebClient.RequestBodyUriSpec requestBodyUriSpec = mock(WebClient.RequestBodyUriSpec.class); final WebClient.ResponseSpec responseSpec = mock(WebClient.ResponseSpec.class); + when(webClientBuilder.build()).thenReturn(webClient); when(webClient.patch()).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.uri(anyString())).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.headers(any())).thenReturn(requestBodyUriSpec); @@ -865,6 +874,14 @@ void publishAdvert_successfullyPublishedAdvert() { when(requestHeadersSpec.retrieve()).thenReturn(responseSpec); when(responseSpec.bodyToMono(Void.class)).thenReturn(Mono.empty()); + when(contentfulEntries.publish(any())).thenReturn(publishedContentfulAdvert); + + final JsonNode mockJsonNode = mock(JsonNode.class); + + when(mapper.valueToTree(any())).thenReturn(mockJsonNode); + + when(mockJsonNode.toString()).thenReturn("{contentfulEntryId: \"entry-id\", action: \"CREATE\"}"); + final ArgumentCaptor entryCaptor = ArgumentCaptor.forClass(CMAEntry.class); final ArgumentCaptor grantAdvertArgumentCaptor = ArgumentCaptor.forClass(GrantAdvert.class); @@ -925,19 +942,27 @@ void publishAdvert_updatesExistingAdvert_IfFirstPublishedDateHasBeenSet() { when(contentfulEntries.update(Mockito.any())).thenReturn(publishedContentfulAdvert); when(contentfulEntries.fetchOne(contentfulAdvertId)).thenReturn(publishedContentfulAdvert, publishedContentfulAdvert); - //when(contentfulEntries.async()).thenReturn(async); doReturn(grantAvertInDatabase).when(grantAdvertService).save(any()); + final WebClient webClient = mock(WebClient.class); final WebClient.RequestHeadersSpec requestHeadersSpec = mock(WebClient.RequestHeadersSpec.class); final WebClient.RequestBodyUriSpec requestBodyUriSpec = mock(WebClient.RequestBodyUriSpec.class); final WebClient.ResponseSpec responseSpec = mock(WebClient.ResponseSpec.class); + when(webClientBuilder.build()).thenReturn(webClient); when(webClient.patch()).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.uri(anyString())).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.headers(any())).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.bodyValue(any())).thenReturn(requestHeadersSpec); when(requestHeadersSpec.retrieve()).thenReturn(responseSpec); when(responseSpec.bodyToMono(Void.class)).thenReturn(Mono.empty()); + when(contentfulEntries.publish(any())).thenReturn(publishedContentfulAdvert); + + final JsonNode mockJsonNode = mock(JsonNode.class); + + when(mapper.valueToTree(any())).thenReturn(mockJsonNode); + + when(mockJsonNode.toString()).thenReturn("{contentfulEntryId: \"entry-id\", action: \"CREATE\"}"); final ArgumentCaptor grantAdvertArgumentCaptor = ArgumentCaptor.forClass(GrantAdvert.class); @@ -976,10 +1001,12 @@ void publishAdvertThroughLambda_successfullyPublishedAdvert() { .grantAdvertName("Grant Advert Name").response(response).grantAdvertName("Homelessness Grant") .build(); + final WebClient webClient = mock(WebClient.class); final WebClient.RequestHeadersSpec requestHeadersSpec = mock(WebClient.RequestHeadersSpec.class); final WebClient.RequestBodyUriSpec requestBodyUriSpec = mock(WebClient.RequestBodyUriSpec.class); final WebClient.ResponseSpec responseSpec = mock(WebClient.ResponseSpec.class); + when(webClientBuilder.build()).thenReturn(webClient); when(webClient.patch()).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.uri(anyString())).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.headers(any())).thenReturn(requestBodyUriSpec); @@ -1007,7 +1034,13 @@ void publishAdvertThroughLambda_successfullyPublishedAdvert() { when(contentfulEntries.fetchOne(contentfulAdvertId)).thenReturn(publishedContentfulAdvert); - //when(contentfulEntries.async()).thenReturn(async); + when(contentfulEntries.publish(any())).thenReturn(publishedContentfulAdvert); + + final JsonNode mockJsonNode = mock(JsonNode.class); + + when(mapper.valueToTree(any())).thenReturn(mockJsonNode); + + when(mockJsonNode.toString()).thenReturn("{contentfulEntryId: \"entry-id\", action: \"CREATE\"}"); final ArgumentCaptor entryCaptor = ArgumentCaptor.forClass(CMAEntry.class); @@ -1076,6 +1109,12 @@ void unpublishAdvert_UnpublishesAdvertFromContentful_AndSetsStatusToDraftInDb() doReturn(advert).when(grantAdvertService).save(any()); when(contentfulAdvert.isPublished()).thenReturn(true); + final JsonNode mockJsonNode = mock(JsonNode.class); + + when(mapper.valueToTree(any())).thenReturn(mockJsonNode); + + when(mockJsonNode.toString()).thenReturn("{contentfulEntryId: \"entry-id\", action: \"CREATE\"}"); + final ArgumentCaptor advertCaptor = ArgumentCaptor.forClass(GrantAdvert.class); // maybe overkill to check this here but ensures we can be sure the state has changed @@ -1084,7 +1123,6 @@ void unpublishAdvert_UnpublishesAdvertFromContentful_AndSetsStatusToDraftInDb() grantAdvertService.unpublishAdvert(grantAdvertId); verify(contentfulEntries).unPublish(contentfulAdvert); - verify(openSearchService).removeIndexEntry(contentfulAdvert); verify(grantAdvertService).save(advertCaptor.capture()); assertThat(advertCaptor.getValue().getId()).isEqualTo(grantAdvertId); @@ -1107,6 +1145,11 @@ void unpublishAdvertThroughLambda_successfullyUnpublishedAdvert() { doReturn(advert).when(grantAdvertService).save(any()); when(contentfulAdvert.isPublished()).thenReturn(true); + final JsonNode mockJsonNode = mock(JsonNode.class); + when(mapper.valueToTree(any())).thenReturn(mockJsonNode); + + when(mockJsonNode.toString()).thenReturn("{contentfulEntryId: \"entry-id\", action: \"CREATE\"}"); + final ArgumentCaptor advertCaptor = ArgumentCaptor.forClass(GrantAdvert.class); // maybe overkill to check this here but ensures we can be sure the state has changed @@ -1115,7 +1158,6 @@ void unpublishAdvertThroughLambda_successfullyUnpublishedAdvert() { grantAdvertService.unpublishAdvert(grantAdvertId); verify(contentfulEntries).unPublish(contentfulAdvert); - verify(openSearchService).removeIndexEntry(contentfulAdvert); verify(grantAdvertService).save(advertCaptor.capture()); assertThat(advertCaptor.getValue().getId()).isEqualTo(grantAdvertId); diff --git a/src/test/java/gov/cabinetoffice/gap/adminbackend/services/OpenSearchServiceTest.java b/src/test/java/gov/cabinetoffice/gap/adminbackend/services/OpenSearchServiceTest.java deleted file mode 100644 index af556dc9..00000000 --- a/src/test/java/gov/cabinetoffice/gap/adminbackend/services/OpenSearchServiceTest.java +++ /dev/null @@ -1,121 +0,0 @@ -package gov.cabinetoffice.gap.adminbackend.services; - -import com.contentful.java.cma.model.CMAEntry; -import com.contentful.java.cma.model.CMASystem; -import gov.cabinetoffice.gap.adminbackend.config.ContentfulConfigProperties; -import gov.cabinetoffice.gap.adminbackend.config.OpenSearchConfig; -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 static org.mockito.Mockito.*; -import org.mockito.Spy; -import org.springframework.http.HttpMethod; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Mono; - -@SpringJUnitConfig -class OpenSearchServiceTest { - @Spy - @InjectMocks - private OpenSearchService openSearchService; - - @Mock - private OpenSearchConfig openSearchConfig; - - @Mock - private WebClient webClient; - - @Mock - private ContentfulConfigProperties contentfulProperties; - - private CMAEntry contentfulEntry; - - @BeforeEach - void beforeEach() { - when(openSearchConfig.getUrl()).thenReturn("testUrl"); - when(openSearchConfig.getDomain()).thenReturn("testDomain"); - when(openSearchConfig.getUsername()).thenReturn("testUsername"); - when(openSearchConfig.getPassword()).thenReturn("testPassword"); - - contentfulEntry = new CMAEntry(); - final CMASystem system = new CMASystem(); - system.setId("testId"); - contentfulEntry.setSystem(system); - } - - @Test - void indexEntry() { - final WebClient.RequestBodyUriSpec mockRequestBodyUriSpec = Mockito.mock(WebClient.RequestBodyUriSpec.class); - final WebClient.RequestHeadersSpec mockRequestHeadersSpec = mock(WebClient.RequestHeadersSpec.class); - final WebClient.RequestHeadersUriSpec mockRequestHeadersUriSpec = mock(WebClient.RequestHeadersUriSpec.class); - final WebClient.ResponseSpec mockResponseSpec = mock(WebClient.ResponseSpec.class); - - when(contentfulProperties.getSpaceId()).thenReturn("Space"); - when(contentfulProperties.getEnvironmentId()).thenReturn("environment"); - when(contentfulProperties.getAccessToken()).thenReturn("accessToken"); - when(contentfulProperties.getDeliveryAPIAccessToken()).thenReturn("deliveryAccessToken"); - - when(webClient.get()).thenReturn(mockRequestHeadersUriSpec); - when(mockRequestHeadersUriSpec.uri(anyString())).thenReturn(mockRequestHeadersUriSpec); - when(mockRequestHeadersUriSpec.headers(any())).thenReturn(mockRequestHeadersUriSpec); - when(mockRequestHeadersUriSpec.retrieve()).thenReturn(mockResponseSpec); - when(mockResponseSpec.onStatus(any(), any())).thenReturn(mockResponseSpec); - when(mockResponseSpec.bodyToMono(String.class)).thenReturn(Mono.just("{\"system\":{\"id\":\"testId\"},\"environmentId\":\"master\",\"id\":\"testId\",\"published\":false,\"archived\":false}")); - - when(webClient.put()).thenReturn(mockRequestBodyUriSpec); - when(mockRequestBodyUriSpec.uri("testUrl/testDomain/_doc/testId")).thenReturn(mockRequestBodyUriSpec); - when(mockRequestBodyUriSpec.body(any(), eq(String.class))).thenReturn(mockRequestHeadersSpec); - when(mockRequestHeadersSpec.header("Content-Type", "application/json; UTF-8")).thenReturn(mockRequestHeadersSpec); - when(mockRequestHeadersSpec.header("Authorization", "Basic dGVzdFVzZXJuYW1lOnRlc3RQYXNzd29yZA==")).thenReturn(mockRequestHeadersSpec); - when(mockRequestHeadersSpec.retrieve()).thenReturn(mockResponseSpec); - when(mockResponseSpec.bodyToMono(void.class)).thenReturn(Mono.empty()); - - openSearchService.indexEntry(contentfulEntry); - - verify(webClient.get(), times(1)) - .uri("https://api.contentful.com/spaces/Space/environments/environment/entries/testId"); - - verify(webClient.put(), times(1)) - .uri("testUrl/testDomain/_doc/testId"); - } - - @Test - void removeIndexEntry() { - final WebClient.RequestBodyUriSpec mockRequestBodyUriSpec = Mockito.mock(WebClient.RequestBodyUriSpec.class); - final WebClient.RequestHeadersSpec mockRequestHeadersSpec = mock(WebClient.RequestHeadersSpec.class); - final WebClient.ResponseSpec mockResponseSpec = mock(WebClient.ResponseSpec.class); - final WebClient.RequestHeadersUriSpec mockRequestHeadersUriSpec = mock(WebClient.RequestHeadersUriSpec.class); - - when(contentfulProperties.getSpaceId()).thenReturn("Space"); - when(contentfulProperties.getEnvironmentId()).thenReturn("environment"); - when(contentfulProperties.getAccessToken()).thenReturn("accessToken"); - when(contentfulProperties.getDeliveryAPIAccessToken()).thenReturn("deliveryAccessToken"); - - - when(webClient.get()).thenReturn(mockRequestHeadersUriSpec); - when(mockRequestHeadersUriSpec.uri(anyString())).thenReturn(mockRequestHeadersUriSpec); - when(mockRequestHeadersUriSpec.headers(any())).thenReturn(mockRequestHeadersUriSpec); - when(mockRequestHeadersUriSpec.retrieve()).thenReturn(mockResponseSpec); - when(mockResponseSpec.onStatus(any(), any())).thenReturn(mockResponseSpec); - when(mockResponseSpec.bodyToMono(String.class)).thenReturn(Mono.just("{\"system\":{\"id\":\"testId\"},\"environmentId\":\"master\",\"id\":\"testId\",\"published\":false,\"archived\":false}")); - - when(webClient.method(HttpMethod.DELETE)).thenReturn(mockRequestBodyUriSpec); - when(mockRequestBodyUriSpec.uri("testUrl/testDomain/_doc/testId")).thenReturn(mockRequestBodyUriSpec); - when(mockRequestBodyUriSpec.body(any(), eq(String.class))).thenReturn(mockRequestHeadersSpec); - when(mockRequestHeadersSpec.header("Content-Type", "application/json; UTF-8")).thenReturn(mockRequestHeadersSpec); - when(mockRequestHeadersSpec.header("Authorization", "Basic dGVzdFVzZXJuYW1lOnRlc3RQYXNzd29yZA==")).thenReturn(mockRequestHeadersSpec); - when(mockRequestHeadersSpec.retrieve()).thenReturn(mockResponseSpec); - when(mockResponseSpec.bodyToMono(void.class)).thenReturn(Mono.empty()); - - openSearchService.removeIndexEntry(contentfulEntry); - - verify(webClient.get(), times(1)) - .uri("https://api.contentful.com/spaces/Space/environments/environment/entries/testId"); - - verify(webClient.method(HttpMethod.DELETE), times(1)) - .uri("testUrl/testDomain/_doc/testId"); - } -}