Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,41 +36,49 @@ public class SdsClient {
private String supplierOdsCode;

public Mono<String> callForGetAsid(String interactionId, String fromOdsCode, String correlationId) {
LOGGER.info("SDS lookup for consumer ASID (fromOdsCode={}, interactionId={}, correlationId={})",
fromOdsCode, interactionId, correlationId);
var sdsDeviceRequest = sdsRequestBuilder.buildAsDeviceAsidRequest(fromOdsCode, supplierOdsCode, interactionId, correlationId);
return retrieveAsDeviceNhsSpineAsid(sdsDeviceRequest, LOOKUP_CONTEXT_CONSUMER_ASID);
}

public Mono<SdsResponseData> callForGetStructuredRecord(String fromOdsCode, String correlationId) {
LOGGER.info("SDS lookup for GetStructuredRecord (fromOdsCode={}, correlationId={})", fromOdsCode, correlationId);
var sdsDeviceRequest = sdsRequestBuilder.buildGetStructuredRecordAsDeviceRequest(fromOdsCode, correlationId);
var sdsEndpointRequest = sdsRequestBuilder.buildGetStructuredRecordEndpointRequest(fromOdsCode, correlationId);
return retrieveData(sdsDeviceRequest, sdsEndpointRequest);
}

public Mono<SdsResponseData> callForMigrateStructuredRecord(String fromOdsCode, String correlationId) {
LOGGER.info("SDS lookup for MigrateStructuredRecord (fromOdsCode={}, correlationId={})", fromOdsCode, correlationId);
var sdsDeviceRequest = sdsRequestBuilder.buildMigrateStructuredRecordAsDeviceRequest(fromOdsCode, correlationId);
var sdsEndpointRequest = sdsRequestBuilder.buildMigrateStructuredRecordEndpointRequest(fromOdsCode, correlationId);
return retrieveData(sdsDeviceRequest, sdsEndpointRequest);
}

public Mono<SdsResponseData> callForPatientSearchAccessDocument(String fromOdsCode, String correlationId) {
LOGGER.info("SDS lookup for PatientSearchAccessDocument (fromOdsCode={}, correlationId={})", fromOdsCode, correlationId);
var sdsDeviceRequest = sdsRequestBuilder.buildPatientSearchAccessDocumentAsDeviceRequest(fromOdsCode, correlationId);
var sdsEndpointRequest = sdsRequestBuilder.buildPatientSearchAccessDocumentEndpointRequest(fromOdsCode, correlationId);
return retrieveData(sdsDeviceRequest, sdsEndpointRequest);
}

public Mono<SdsResponseData> callForSearchForDocumentRecord(String fromOdsCode, String correlationId) {
LOGGER.info("SDS lookup for SearchForDocument (fromOdsCode={}, correlationId={})", fromOdsCode, correlationId);
var sdsDeviceRequest = sdsRequestBuilder.buildSearchForDocumentAsDeviceRequest(fromOdsCode, correlationId);
var sdsEndpointRequest = sdsRequestBuilder.buildSearchForDocumentEndpointRequest(fromOdsCode, correlationId);
return retrieveData(sdsDeviceRequest, sdsEndpointRequest);
}

public Mono<SdsResponseData> callForRetrieveDocumentRecord(String fromOdsCode, String correlationId) {
LOGGER.info("SDS lookup for RetrieveDocument (fromOdsCode={}, correlationId={})", fromOdsCode, correlationId);
var sdsDeviceRequest = sdsRequestBuilder.buildRetrieveDocumentAsDeviceRequest(fromOdsCode, correlationId);
var sdsEndpointRequest = sdsRequestBuilder.buildRetrieveDocumentEndpointRequest(fromOdsCode, correlationId);
return retrieveData(sdsDeviceRequest, sdsEndpointRequest);
}

public Mono<SdsResponseData> callForMigrateDocumentRecord(String fromOdsCode, String correlationId) {
LOGGER.info("SDS lookup for MigrateDocument (fromOdsCode={}, correlationId={})", fromOdsCode, correlationId);
var sdsDeviceRequest = sdsRequestBuilder.buildMigrateDocumentAsDeviceRequest(fromOdsCode, correlationId);
var sdsEndpointRequest = sdsRequestBuilder.buildMigrateDocumentEndpointRequest(fromOdsCode, correlationId);
return retrieveData(sdsDeviceRequest, sdsEndpointRequest);
Expand All @@ -86,6 +94,11 @@ private Mono<SdsResponseData> retrieveData(RequestHeadersSpec<? extends RequestH
.map(bundle -> {
doBundleEntryCheck(bundle, LOOKUP_CONTEXT_PROVIDER_ENDPOINT);
var endpoint = (Endpoint) bundle.getEntryFirstRep().getResource();
var nhsMhsId = getNhsMhsId(endpoint);
var address = getAddressFromEndpoint(endpoint);

LOGGER.info("SDS provider details retrieved (nhsMhsId={}, nhsSpineAsid={}, address={})",
nhsMhsId, nhsSpineAsid, address);

return SdsResponseData.builder()
.address(getAddressFromEndpoint(endpoint))
Expand Down Expand Up @@ -165,14 +178,13 @@ private String getAddressFromEndpoint(Endpoint endpoint) {
LOGGER.error("SDS Endpoint response contained an empty address");
throw new RuntimeException("SDS returned a result but with an empty address");
}
LOGGER.info("Found GPC provider endpoint in SDS: {}", address);
return address;
}

private Mono<String> performRequest(RequestHeadersSpec<? extends RequestHeadersSpec<?>> request) {
return request.retrieve()
.bodyToMono(String.class)
.doOnError(e -> LOGGER.error("SDS request failed: {}", e));
.doOnError(e -> LOGGER.error("SDS request failed", e));
}

@Builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class SdsRequestBuilder {

private static final String PIPE = "|";
private static final String ORG_CODE_PARAMETER = "organization";
private static final String ORG_CODE_IDENTIFIER = "https://fhir.nhs.uk/Id/ods-organization-code";
Expand Down Expand Up @@ -121,9 +122,13 @@ private RequestHeadersSpec<? extends RequestHeadersSpec<?>> buildAsidClientFor(S
String interaction, String correlationId) {

if (StringUtils.isEmpty(supplierOdsCode)) {
LOGGER.error("Supplier ODS code is not configured — cannot build ASID lookup request");
throw new GpConnectException("Supplier ODS code variable must be defined");
}

LOGGER.debug("Building ASID Device request (consumerOdsCode={}, supplierOdsCode={}, interaction={})",
consumerOrgOdsCode, supplierOdsCode, interaction);

var httpClient = getHttpClient();

return buildWebClient(httpClient)
Expand All @@ -141,6 +146,7 @@ private RequestHeadersSpec<? extends RequestHeadersSpec<?>> buildAsidClientFor(S
@NotNull
private RequestHeadersSpec<? extends RequestHeadersSpec<?>> buildClientFor(String odsCode, String interaction,
String correlationId, String path) {
LOGGER.debug("Building SDS request (odsCode={}, interaction={}, path={})", odsCode, interaction, path);
var httpClient = getHttpClient();

return buildWebClient(httpClient)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package uk.nhs.adaptors.gpc.consumer.sds.builder;

import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.NullAndEmptySource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeStrategies;

import io.netty.handler.ssl.SslContext;
import reactor.core.publisher.Mono;
import uk.nhs.adaptors.gpc.consumer.gpc.exception.GpConnectException;
import uk.nhs.adaptors.gpc.consumer.sds.configuration.SdsConfiguration;
import uk.nhs.adaptors.gpc.consumer.web.RequestBuilderService;
import uk.nhs.adaptors.gpc.consumer.web.WebClientFilterService;
import static org.junit.jupiter.api.Assertions.assertNotNull;

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class SdsRequestBuilderTest {

private static final String ODS_CODE = "A12345";
private static final String SUPPLIER_ODS_CODE = "SUPPLIER01";
private static final String CORRELATION_ID = "corr-id-001";
private static final String INTERACTION_ID = "urn:nhs:names:services:gpconnect:fhir:operation:gpc.getstructuredrecord-1";
private static final String SDS_URL = "https://sds.example.com";
private static final String API_KEY = "test-api-key";

@Mock
private SdsConfiguration sdsConfiguration;

@Mock
private RequestBuilderService requestBuilderService;

@Mock
private WebClientFilterService webClientFilterService;

@Mock
private SslContext sslContext;

private SdsRequestBuilder sdsRequestBuilder;

@BeforeEach
void setUp() {
sdsRequestBuilder = new SdsRequestBuilder(sdsConfiguration, requestBuilderService, webClientFilterService);
when(requestBuilderService.buildStandardSslContext()).thenReturn(sslContext);
when(requestBuilderService.buildExchangeStrategies()).thenReturn(ExchangeStrategies.withDefaults());
when(sdsConfiguration.getUrl()).thenReturn(SDS_URL);
when(sdsConfiguration.getApiKey()).thenReturn(API_KEY);
when(webClientFilterService.logRequest())
.thenReturn((request, next) -> next.exchange(request));
when(webClientFilterService.logResponse())
.thenReturn(ExchangeFilterFunction.ofResponseProcessor(Mono::just));
when(webClientFilterService.errorHandlingFilter(any(), any()))
.thenReturn(ExchangeFilterFunction.ofResponseProcessor(Mono::just));
}

@ParameterizedTest
@NullAndEmptySource
void When_SupplierOdsCodeIsBlank_Expect_GpConnectException(String blankSupplierOdsCode) {
assertThatThrownBy(() -> sdsRequestBuilder.buildAsDeviceAsidRequest(ODS_CODE, blankSupplierOdsCode, INTERACTION_ID, CORRELATION_ID))
.isInstanceOf(GpConnectException.class)
.hasMessageContaining("Supplier ODS code variable must be defined");
}

@Test
void When_SupplierOdsCodeIsPresent_Expect_BuildAsDeviceAsidRequestReturnsRequest() {

var result = sdsRequestBuilder.buildAsDeviceAsidRequest(ODS_CODE, SUPPLIER_ODS_CODE, INTERACTION_ID, CORRELATION_ID);

assertNotNull(result);
}

@Test
void When_Called_Expect_BuildGetStructuredRecordAsDeviceRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildGetStructuredRecordAsDeviceRequest(ODS_CODE, CORRELATION_ID));
}

@Test
void When_Called_Expect_BuildGetStructuredRecordEndpointRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildGetStructuredRecordEndpointRequest(ODS_CODE, CORRELATION_ID));
}

@Test
void When_Called_Expect_BuildMigrateStructuredRecordAsDeviceRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildMigrateStructuredRecordAsDeviceRequest(ODS_CODE, CORRELATION_ID));
}

@Test
void When_Called_Expect_BuildMigrateStructuredRecordEndpointRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildMigrateStructuredRecordEndpointRequest(ODS_CODE, CORRELATION_ID));
}

@Test
void When_Called_Expect_BuildPatientSearchAccessDocumentAsDeviceRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildPatientSearchAccessDocumentAsDeviceRequest(ODS_CODE, CORRELATION_ID));
}

@Test
void When_Called_Expect_BuildPatientSearchAccessDocumentEndpointRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildPatientSearchAccessDocumentEndpointRequest(ODS_CODE, CORRELATION_ID));
}

@Test
void When_Called_Expect_BuildSearchForDocumentAsDeviceRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildSearchForDocumentAsDeviceRequest(ODS_CODE, CORRELATION_ID));
}

@Test
void When_Called_Expect_BuildSearchForDocumentEndpointRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildSearchForDocumentEndpointRequest(ODS_CODE, CORRELATION_ID));
}

@Test
void When_Called_Expect_BuildRetrieveDocumentAsDeviceRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildRetrieveDocumentAsDeviceRequest(ODS_CODE, CORRELATION_ID));
}

@Test
void When_Called_Expect_BuildRetrieveDocumentEndpointRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildRetrieveDocumentEndpointRequest(ODS_CODE, CORRELATION_ID));
}

@Test
void When_Called_Expect_BuildMigrateDocumentAsDeviceRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildMigrateDocumentAsDeviceRequest(ODS_CODE, CORRELATION_ID));
}

@Test
void When_Called_Expect_BuildMigrateDocumentEndpointRequestReturnsRequest() {
assertNotNull(sdsRequestBuilder.buildMigrateDocumentEndpointRequest(ODS_CODE, CORRELATION_ID));
}
}
Loading