From 15da6cef23a37c3f552010b7def81d89269f694c Mon Sep 17 00:00:00 2001 From: michaeljacob Date: Mon, 23 Mar 2026 16:38:19 +0000 Subject: [PATCH 1/8] validate callback url --- build.gradle | 2 + charts/em-stitching/values.yaml | 3 + .../functional/DocumentTaskScenarios.java | 185 +++++++++--------- .../SecureDocumentTaskScenarios.java | 27 +-- .../testutil/CallbackMockConfig.java | 46 +++++ .../em/stitching/testutil/TestUtil.java | 16 ++ .../validation/CallableEndpointValidator.java | 72 +++---- src/main/resources/application.yaml | 5 + src/main/resources/messages.properties | 2 +- .../CallableEndpointValidatorTest.java | 111 ++++------- 10 files changed, 262 insertions(+), 207 deletions(-) create mode 100644 src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/CallbackMockConfig.java diff --git a/build.gradle b/build.gradle index 34c5e8f3d..b2ed03ad1 100644 --- a/build.gradle +++ b/build.gradle @@ -225,6 +225,8 @@ dependencies { testImplementation group: 'com.github.hmcts', name: 'fortify-client', version: '1.4.10', classifier: 'all' + testImplementation group: 'org.wiremock', name: 'wiremock-standalone', version: '3.13.2' + aatRuntimeOnly group: 'jakarta.xml.bind', name: 'jakarta.xml.bind-api', version: '4.0.5' aatRuntimeOnly group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '4.0.7' diff --git a/charts/em-stitching/values.yaml b/charts/em-stitching/values.yaml index 96916eb49..8a7a62dbf 100644 --- a/charts/em-stitching/values.yaml +++ b/charts/em-stitching/values.yaml @@ -37,6 +37,7 @@ java: DOCMOSIS_ENDPOINT: https://docmosis.{{ .Values.global.environment }}.platform.hmcts.net/rs/convert DOCMOSIS_RENDER_ENDPOINT: https://docmosis.{{ .Values.global.environment }}.platform.hmcts.net/rs/render CDAM_URL: http://ccd-case-document-am-api-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal + CALLBACK_DOMAIN: em-ccd-orchestrator-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal ENABLE_DB_MIGRATE: false REFORM_SERVICE_NAME: rpa-em-stitching-api @@ -67,3 +68,5 @@ java: HTTP_CLIENT_CONNECT_TIMEOUT: 30000 HTTP_CLIENT_SOCKET_TIMEOUT: 60000 APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL: INFO + CALLBACK_HTTP_SCHEME: http + CALLBACK_HTTP_HOST_PORT: 8080 diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java index d548ef1dd..b66273502 100644 --- a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java @@ -9,10 +9,12 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; import uk.gov.hmcts.reform.em.stitching.domain.enumeration.TaskState; import uk.gov.hmcts.reform.em.stitching.service.dto.BundleDTO; import uk.gov.hmcts.reform.em.stitching.service.dto.CallbackDto; import uk.gov.hmcts.reform.em.stitching.service.dto.DocumentTaskDTO; +import uk.gov.hmcts.reform.em.stitching.testutil.CallbackMockConfig; import uk.gov.hmcts.reform.em.stitching.testutil.TestUtil; import java.io.File; @@ -25,13 +27,13 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static uk.gov.hmcts.reform.em.stitching.testutil.TestUtil.convertObjectToJsonBytes; - +@Import(CallbackMockConfig.class) class DocumentTaskScenarios extends BaseTest { private RequestSpecification request; private RequestSpecification unAuthenticatedRequest; + private static final String END_POINT = "/api/document-tasks"; - private static final String CALL_BACK_URL = "https://postman-echo.com/post"; private static final String TASK_STATE = "taskState"; private static final String BUNDLE_S_DOC_URI = "bundle.stitchedDocumentURI"; @@ -43,14 +45,14 @@ protected DocumentTaskScenarios(TestUtil testUtil) { @BeforeEach public void setupRequestSpecification() { request = testUtil - .authRequest() - .baseUri(testUtil.getTestUrl()) - .contentType(APPLICATION_JSON_VALUE); + .authRequest() + .baseUri(testUtil.getTestUrl()) + .contentType(APPLICATION_JSON_VALUE); unAuthenticatedRequest = testUtil - .unauthenticatedRequest() - .baseUri(testUtil.getTestUrl()) - .contentType(APPLICATION_JSON_VALUE); + .unauthenticatedRequest() + .baseUri(testUtil.getTestUrl()) + .contentType(APPLICATION_JSON_VALUE); } @Test @@ -60,9 +62,9 @@ void testPostBundleStitch() throws IOException, InterruptedException { documentTask.setBundle(bundle); Response createTaskResponse = - request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); assertEquals(201, createTaskResponse.getStatusCode()); String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); @@ -80,11 +82,10 @@ void testPostBundleStitchWithCaseId() throws IOException, InterruptedException { String testCaseId = "TestCaseId967"; documentTask.setCaseId(testCaseId); - Response createTaskResponse = - request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); assertEquals(201, createTaskResponse.getStatusCode()); assertEquals(testCaseId, createTaskResponse.getBody().jsonPath().getString("caseId")); @@ -93,7 +94,6 @@ void testPostBundleStitchWithCaseId() throws IOException, InterruptedException { assertEquals(200, getTaskResponse.getStatusCode()); assertNotNull(getTaskResponse.getBody().jsonPath().getString(BUNDLE_S_DOC_URI)); - } @Test @@ -103,9 +103,9 @@ void testPostBundleStitchWithWordDoc() throws IOException, InterruptedException documentTask.setBundle(bundle); Response createTaskResponse = - request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); assertEquals(201, createTaskResponse.getStatusCode()); String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); @@ -122,9 +122,9 @@ void testPostBundleStitchWithTextFile() throws IOException, InterruptedException documentTask.setBundle(bundle); Response createTaskResponse = - request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); assertEquals(201, createTaskResponse.getStatusCode()); String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); @@ -141,9 +141,9 @@ void testPostBundleStitchWithRichTextFile() throws IOException, InterruptedExcep documentTask.setBundle(bundle); Response createTaskResponse = - request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); assertEquals(201, createTaskResponse.getStatusCode()); String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); @@ -161,9 +161,9 @@ void testPostBundleStitchWithExcelAndPpt() throws IOException, InterruptedExcept documentTask.setBundle(bundle); Response createTaskResponse = - request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); assertEquals(201, createTaskResponse.getStatusCode()); String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); @@ -180,9 +180,9 @@ void testPostBundleStitchWithImage() throws IOException, InterruptedException { documentTask.setBundle(bundle); Response createTaskResponse = - request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); assertEquals(201, createTaskResponse.getStatusCode()); String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); @@ -199,9 +199,9 @@ void testPostBundleStitchWithDocumentWatermarkImage() throws IOException, Interr documentTask.setBundle(bundle); Response createTaskResponse = - request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); assertEquals(201, createTaskResponse.getStatusCode()); String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); @@ -230,9 +230,9 @@ void testStitchTwoIdenticalDocuments() throws IOException, InterruptedException documentTask.setBundle(bundle); Response createTaskResponse = - request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); assertEquals(201, createTaskResponse.getStatusCode()); String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); @@ -249,9 +249,9 @@ void testStitchDocumentsWithSortIndices() throws IOException, InterruptedExcepti documentTask.setBundle(bundle); Response createTaskResponse = - request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); Response completedResponse = testUtil.pollUntil(taskUrl, body -> body.getString(TASK_STATE).equals("DONE")); @@ -270,37 +270,39 @@ void testStitchDocumentsWithSortIndices() throws IOException, InterruptedExcepti assertTrue(indexOfDocument2 < indexOfDocument1); } - @Test void testPostBundleStitchWithCallback() throws IOException, InterruptedException { + String validCallbackUrl = testUtil.getValidCallbackUrl(); BundleDTO bundle = testUtil.getTestBundle(); DocumentTaskDTO documentTask = new DocumentTaskDTO(); documentTask.setBundle(bundle); CallbackDto callback = new CallbackDto(); - callback.setCallbackUrl(CALL_BACK_URL); + callback.setCallbackUrl(validCallbackUrl); documentTask.setCallback(callback); Response createTaskResponse = - request - .log().all() - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .log().all() + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); + assertEquals(201, createTaskResponse.getStatusCode()); - assertEquals(CALL_BACK_URL, - createTaskResponse.getBody().jsonPath().getString("callback.callbackUrl")); + assertEquals(validCallbackUrl, createTaskResponse.getBody().jsonPath().getString("callback.callbackUrl")); String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); - testUtil.pollUntil(taskUrl, body -> body.getString("callback.callbackState").equals("SUCCESS")); + testUtil.pollUntil(taskUrl, body -> body.getString("callback.callbackState").equals("SUCCESS")); } @Test void testPostBundleStitchWithCallbackForFailure() throws IOException { + String validCallbackUrl = testUtil.getValidCallbackUrl(); + CallbackDto callback = new CallbackDto(); - callback.setCallbackUrl(CALL_BACK_URL); + callback.setCallbackUrl(validCallbackUrl); callback.setCreatedBy("callback_dummy1"); callback.setCreatedDate(Instant.now()); callback.setLastModifiedBy("callback_dummmy2"); @@ -317,38 +319,39 @@ void testPostBundleStitchWithCallbackForFailure() throws IOException { documentTask.setLastModifiedDate(Instant.now()); Response createTaskResponse = - request - .log().all() - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .log().all() + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); + assertEquals(400, createTaskResponse.getStatusCode()); assertTrue(createTaskResponse.body().asString().contains("Error saving Document Task")); assertTrue(createTaskResponse.getBody().jsonPath().getString("detail") - .contains("Bundle Title can not be more than 255 Chars")); + .contains("Bundle Title can not be more than 255 Chars")); } @Test - void testPostBundleStitchWithCallbackUrlNotAccessible() throws IOException { + void testPostBundleStitchWithInvalidCallbackUrl() throws IOException { BundleDTO bundle = testUtil.getTestBundle(); DocumentTaskDTO documentTask = new DocumentTaskDTO(); documentTask.setBundle(bundle); CallbackDto callback = new CallbackDto(); - callback.setCallbackUrl("http://localhost:80899/my/callback/resource"); + callback.setCallbackUrl("http://invalid-format-domain.com/wrong/path"); documentTask.setCallback(callback); Response createTaskResponse = - request - .log().all() - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT); + request + .log().all() + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT); createTaskResponse.prettyPrint(); assertEquals(400, createTaskResponse.getStatusCode()); assertEquals("callback.callbackUrl", createTaskResponse.getBody().jsonPath().getString("fieldErrors[0].field")); - assertEquals("Connection to the callback URL could not be verified.", + assertEquals("Callback URL must be a valid internal endpoint.", createTaskResponse.getBody().jsonPath().getString("fieldErrors[0].message")); } @@ -360,11 +363,11 @@ void shouldReturn401WhenUnAuthenticatedUserPostBundleStitch() throws IOException documentTask.setBundle(bundle); unAuthenticatedRequest - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT) - .then() - .assertThat() - .statusCode(401); + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT) + .then() + .assertThat() + .statusCode(401); } @Test @@ -373,19 +376,19 @@ void shouldReturn404WhenGetDocumentTaskWithNonExistentId() throws IOException { DocumentTaskDTO documentTask = new DocumentTaskDTO(); documentTask.setBundle(bundle); request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT) - .then().log().all() - .assertThat() - .statusCode(201); + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT) + .then().log().all() + .assertThat() + .statusCode(201); final long nonExistentId = Long.MAX_VALUE; final String taskUrl = END_POINT + "/" + nonExistentId; request - .get(taskUrl) - .then().log().all() - .assertThat() - .statusCode(404); + .get(taskUrl) + .then().log().all() + .assertThat() + .statusCode(404); } @Test @@ -395,22 +398,22 @@ void shouldReturn401WhenUnAuthenticatedUserGetDocumentTask() throws IOException documentTask.setBundle(bundle); final String documentTaskId = - request - .body(convertObjectToJsonBytes(documentTask)) - .post(END_POINT) - .then() - .assertThat() - .statusCode(201) - .extract() - .jsonPath() - .getString("id"); + request + .body(convertObjectToJsonBytes(documentTask)) + .post(END_POINT) + .then() + .assertThat() + .statusCode(201) + .extract() + .jsonPath() + .getString("id"); final String taskUrl = END_POINT + "/" + documentTaskId; unAuthenticatedRequest - .get(taskUrl) - .then().log().all() - .assertThat() - .statusCode(401); + .get(taskUrl) + .then().log().all() + .assertThat() + .statusCode(401); } @Test @@ -431,7 +434,7 @@ void testPostBundleStitchWithInvalidDocumentUri() throws IOException, Interrupte String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); Response failedTaskResponse = testUtil.pollUntil( taskUrl, body -> - body.getString(TASK_STATE).equals("FAILED") + body.getString(TASK_STATE).equals("FAILED") ); assertEquals(200, failedTaskResponse.getStatusCode()); @@ -474,4 +477,4 @@ void testPostBundleStitchWithExternalDomainUrl() throws IOException, Interrupted assertEquals("DONE", getTaskResponse.getBody().jsonPath().getString(TASK_STATE)); assertNotNull(getTaskResponse.getBody().jsonPath().getString(BUNDLE_S_DOC_URI)); } -} +} \ No newline at end of file diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java index a510c36d4..87f8d135d 100644 --- a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java @@ -35,8 +35,6 @@ public class SecureDocumentTaskScenarios extends BaseTest { private static final String HASH_TOKEN_PATH = "bundle.hashToken"; private static final String CALLBACK_URL_PATH = "callback.callbackUrl"; private static final String CALLBACK_STATE_PATH = "callback.callbackState"; - private static final String POSTMAN_ECHO_URL = "https://postman-echo.com/post"; - private RequestSpecification request; private RequestSpecification unAuthenticatedRequest; @@ -65,6 +63,7 @@ public void setupRequestSpecification() { documentTask.setServiceAuth(testUtil.getServiceAuth()); } + @Test void testPostBundleStitch() throws IOException, InterruptedException { BundleDTO bundle = testUtil.getCdamTestBundle(); @@ -248,7 +247,7 @@ void testStitchDocumentsWithSortIndices() throws IOException, InterruptedExcepti String stitchedDocumentUri = completedResponse.getBody().jsonPath().getString(STITCHED_DOC_URI_PATH); - //We need to donwload the Stitched Document via Dm-Store and not via CDAM. As at this stage the document is + //We need to download the Stitched Document via Dm-Store and not via CDAM. As at this stage the document is // not yet associated to the case through CCD callBack. File stitchedFile = testUtil.downloadDocument(stitchedDocumentUri); @@ -266,12 +265,13 @@ void testStitchDocumentsWithSortIndices() throws IOException, InterruptedExcepti @Test void testPostBundleStitchWithCallback() throws IOException, InterruptedException { + String validCallbackUrl = testUtil.getValidCallbackUrl(); BundleDTO bundle = testUtil.getCdamTestBundle(); documentTask.setBundle(bundle); CallbackDto callback = new CallbackDto(); - callback.setCallbackUrl(POSTMAN_ECHO_URL); + callback.setCallbackUrl(validCallbackUrl); documentTask.setCallback(callback); @@ -280,19 +280,20 @@ void testPostBundleStitchWithCallback() throws IOException, InterruptedException .log().all() .body(convertObjectToJsonBytes(documentTask)) .post(API_DOCUMENT_TASKS); + assertEquals(201, createTaskResponse.getStatusCode()); - assertEquals(POSTMAN_ECHO_URL, - createTaskResponse.getBody().jsonPath().getString(CALLBACK_URL_PATH)); + assertEquals(validCallbackUrl, createTaskResponse.getBody().jsonPath().getString(CALLBACK_URL_PATH)); String taskUrl = API_DOCUMENT_TASKS + "/" + createTaskResponse.getBody().jsonPath().getString(TASK_ID_PATH); testUtil.pollUntil(taskUrl, body -> body.getString(CALLBACK_STATE_PATH).equals("SUCCESS")); - } @Test void testPostBundleStitchWithCallbackForFailure() throws IOException { + String validCallbackUrl = testUtil.getValidCallbackUrl(); + CallbackDto callback = new CallbackDto(); - callback.setCallbackUrl(POSTMAN_ECHO_URL); + callback.setCallbackUrl(validCallbackUrl); callback.setCreatedBy("callback_dummy1"); callback.setCreatedDate(Instant.now()); callback.setLastModifiedBy("callback_dummmy2"); @@ -312,20 +313,20 @@ void testPostBundleStitchWithCallbackForFailure() throws IOException { .log().all() .body(convertObjectToJsonBytes(documentTask)) .post(API_DOCUMENT_TASKS); + assertEquals(400, createTaskResponse.getStatusCode()); assertTrue(createTaskResponse.body().asString().contains("Error saving Document Task")); assertTrue(createTaskResponse.getBody().jsonPath().getString("detail") .contains("Bundle Title can not be more than 255 Chars")); - } @Test - void testPostBundleStitchWithCallbackUrlNotAccessible() throws IOException { + void testPostBundleStitchWithInvalidCallbackUrl() throws IOException { BundleDTO bundle = testUtil.getCdamTestBundle(); documentTask.setBundle(bundle); CallbackDto callback = new CallbackDto(); - callback.setCallbackUrl("http://localhost:80899/my/callback/resource"); + callback.setCallbackUrl("http://invalid-format-domain.com/wrong/path"); documentTask.setCallback(callback); @@ -339,7 +340,7 @@ void testPostBundleStitchWithCallbackUrlNotAccessible() throws IOException { assertEquals(400, createTaskResponse.getStatusCode()); assertEquals(CALLBACK_URL_PATH, createTaskResponse.getBody().jsonPath().getString("fieldErrors[0].field")); - assertEquals("Connection to the callback URL could not be verified.", + assertEquals("Callback URL must be a valid internal endpoint.", createTaskResponse.getBody().jsonPath().getString("fieldErrors[0].message")); } @@ -400,4 +401,4 @@ void shouldReturn401WhenUnAuthenticatedUserGetDocumentTask() throws IOException .assertThat() .statusCode(401); } -} +} \ No newline at end of file diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/CallbackMockConfig.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/CallbackMockConfig.java new file mode 100644 index 000000000..a986f6f76 --- /dev/null +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/CallbackMockConfig.java @@ -0,0 +1,46 @@ +package uk.gov.hmcts.reform.em.stitching.testutil; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.common.ConsoleNotifier; +import com.github.tomakehurst.wiremock.common.FatalStartupException; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; + +@TestConfiguration +public class CallbackMockConfig { + + private static final Logger log = LoggerFactory.getLogger(CallbackMockConfig.class); + + @Value("${callbackurlvalidator.port:8080}") + private int port; + + @Bean(destroyMethod = "stop") + @ConditionalOnProperty(name = "callbackurlvalidator.host", havingValue = "localhost", matchIfMissing = true) + public WireMockServer callbackWireMockServer() { + WireMockServer wireMockServer = new WireMockServer( + WireMockConfiguration.options() + .port(port) + .notifier(new ConsoleNotifier(true)) + ); + + try { + wireMockServer.start(); + wireMockServer.stubFor(post(urlPathMatching("/api/stitching-complete-callback.*")) + .willReturn(aResponse().withStatus(200))); + log.info("WireMock successfully started on port {} for local testing.", port); + } catch (FatalStartupException e) { + log.warn("WireMock could not bind to port {}. Degrading gracefully. Reason: {}", port, e.getMessage()); + } + + return wireMockServer; + } +} \ No newline at end of file diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/TestUtil.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/TestUtil.java index 642aa92f2..d5d7da793 100644 --- a/src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/TestUtil.java +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/TestUtil.java @@ -76,6 +76,15 @@ public class TestUtil { @Value("${document_management.url}") private String dmApiUrl; + @Value("${callbackurlvalidator.scheme:http}") + private String callbackScheme; + + @Value("${callbackurlvalidator.host:localhost}") + private String callbackHost; + + @Value("${callbackurlvalidator.port:8080}") + private int callbackPort; + private final IdamHelper idamHelper; private final S2sHelper s2sHelper; @@ -878,4 +887,11 @@ public BundleDTO getCdamTestBundleWithSortedDocuments() throws JsonProcessingExc bundle.setDocuments(docs); return bundle; } + + public String getValidCallbackUrl() { + String portStr = (callbackPort <= 0) ? "" : ":" + callbackPort; + return String.format("%s://%s%s/api/stitching-complete-callback" + + "/1234567890123456/asyncStitchingComplete/123e4567-e89b-12d3-a456-426614174000", + callbackScheme, callbackHost, portStr); + } } \ No newline at end of file diff --git a/src/main/java/uk/gov/hmcts/reform/em/stitching/domain/validation/CallableEndpointValidator.java b/src/main/java/uk/gov/hmcts/reform/em/stitching/domain/validation/CallableEndpointValidator.java index 5f1df2e58..a8aa0863f 100644 --- a/src/main/java/uk/gov/hmcts/reform/em/stitching/domain/validation/CallableEndpointValidator.java +++ b/src/main/java/uk/gov/hmcts/reform/em/stitching/domain/validation/CallableEndpointValidator.java @@ -2,52 +2,58 @@ import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponentsBuilder; -import java.net.URI; -import java.net.URL; +import java.util.regex.Pattern; -import static uk.gov.hmcts.reform.em.stitching.service.CloseableCloser.close; +import static java.util.Objects.isNull; +@Component public class CallableEndpointValidator implements ConstraintValidator { - private final Logger log = LoggerFactory.getLogger(CallableEndpointValidator.class); + private static final Logger log = LoggerFactory.getLogger(CallableEndpointValidator.class); - private final OkHttpClient okHttpClient; + private static final String CASE_ID_PATTERN = "\\d{16}"; + private static final String TRIGGER_ID = "asyncStitchingComplete"; + private static final String UUID_PATTERN = + "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"; - public CallableEndpointValidator(OkHttpClient okHttpClient) { - this.okHttpClient = okHttpClient; + private final Pattern compiledPattern; + + public CallableEndpointValidator( + @Value("${callbackurlvalidator.scheme}") String scheme, + @Value("${callbackurlvalidator.host}") String host, + @Value("${callbackurlvalidator.port}") int port) { + + String baseUrl = UriComponentsBuilder.newInstance() + .scheme(scheme) + .host(host) + .port(port) + .build() + .toUriString(); + + String regexPath = String.format("/api/stitching-complete-callback/%s/%s/%s", + CASE_ID_PATTERN, TRIGGER_ID, UUID_PATTERN); + + this.compiledPattern = Pattern.compile("^" + Pattern.quote(baseUrl) + regexPath + "$"); } @Override public boolean isValid(String urlString, ConstraintValidatorContext context) { - boolean valid; - Response response = null; - try { - URL url = new URI(urlString).toURL(); - String urlWithoutPathString = String.format("%s://%s:%d", - url.getProtocol(), - url.getHost(), - url.getPort() < 0 ? url.getDefaultPort() : url.getPort()); - log.debug("Probing callback {}", urlWithoutPathString); - URL urlWithoutPath = new URI(urlWithoutPathString).toURL(); - response = okHttpClient - .newCall(new Request.Builder() - .url(urlWithoutPath) - .build()) - .execute(); - valid = response.code() < 500; - } catch (Exception e) { - log.error(String.format("Callback %s could not be verified", urlString), e); - valid = false; - } finally { - close(response); + if (isNull(urlString) || urlString.isBlank()) { + return false; } + + boolean valid = compiledPattern.matcher(urlString).matches(); + + if (!valid) { + log.warn("Callback URL '{}' is invalid or was not generated by the trusted CallbackUrlCreator", urlString); + } + return valid; } - -} +} \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 3650efb1c..e09cef5e2 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -157,6 +157,11 @@ stitching-complete: callback: max-attempts: ${CALLBACK_MAX_ATTEMPTS:3} +callbackurlvalidator: + scheme: ${CALLBACK_HTTP_SCHEME:http} + host: ${CALLBACK_DOMAIN:localhost} + port: ${CALLBACK_HTTP_HOST_PORT:8080} + dbMigration: # When true, the app will run DB migration on startup. # Otherwise, it will just check if all migrations have been applied (and fail to start if not). diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index c8252ec4f..ea633b0ab 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -1,2 +1,2 @@ -CallableEndpoint=Connection to the callback URL could not be verified. +CallableEndpoint=Callback URL must be a valid internal endpoint. NotNull=required diff --git a/src/test/java/uk/gov/hmcts/reform/em/stitching/domain/validation/CallableEndpointValidatorTest.java b/src/test/java/uk/gov/hmcts/reform/em/stitching/domain/validation/CallableEndpointValidatorTest.java index 140f96969..4cb959d82 100644 --- a/src/test/java/uk/gov/hmcts/reform/em/stitching/domain/validation/CallableEndpointValidatorTest.java +++ b/src/test/java/uk/gov/hmcts/reform/em/stitching/domain/validation/CallableEndpointValidatorTest.java @@ -1,98 +1,71 @@ package uk.gov.hmcts.reform.em.stitching.domain.validation; -import okhttp3.Interceptor; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Protocol; -import okhttp3.Response; -import okhttp3.ResponseBody; +import jakarta.validation.ConstraintValidatorContext; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class CallableEndpointValidatorTest { - @Test - void isValidReturn200() { - CallableEndpointValidator callableEndpointValidator = - createValidatorWithMockHttp((Interceptor.Chain chain) -> new Response.Builder() - .body(ResponseBody.create("", MediaType.parse("plain/text"))) - .request(chain.request()) - .message("") - .code(200) - .protocol(Protocol.HTTP_2) - .build()); + private CallableEndpointValidator validator; + private ConstraintValidatorContext mockContext; - assertTrue(callableEndpointValidator.isValid("http://localhost:8089/my/callback/resource", null)); + @BeforeEach + void setUp() { + validator = new CallableEndpointValidator("http", "localhost", 8080); + mockContext = null; } @Test - void isValidReturn400() { - CallableEndpointValidator callableEndpointValidator = - createValidatorWithMockHttp((Interceptor.Chain chain) -> new Response.Builder() - .body(ResponseBody.create("", MediaType.parse("plain/text"))) - .request(chain.request()) - .message("") - .code(400) - .protocol(Protocol.HTTP_2) - .build()); - - assertTrue(callableEndpointValidator.isValid("http://localhost:8089/my/callback/resource", null)); + void isValidReturnsTrueForPerfectMatch() { + String validUrl = "http://localhost:8080/api/stitching-complete-callback/1234567890123456/asyncStitchingComplete/123e4567-e89b-12d3-a456-426614174000"; + assertTrue(validator.isValid(validUrl, mockContext)); } @Test - void isValidReturn500() { - CallableEndpointValidator callableEndpointValidator = - createValidatorWithMockHttp((Interceptor.Chain chain) -> new Response.Builder() - .body(ResponseBody.create("", MediaType.parse("plain/text"))) - .request(chain.request()) - .message("") - .code(500) - .protocol(Protocol.HTTP_2) - .build()); + void isValidReturnsTrueWhenPortIsOmittedByConfig() { + CallableEndpointValidator noPortValidator = new CallableEndpointValidator("https", "my-domain.com", -1); - assertFalse(callableEndpointValidator.isValid("http://localhost:8089/my/callback/resource", null)); + String validUrl = "https://my-domain.com/api/stitching-complete-callback/1234567890123456/asyncStitchingComplete/123e4567-e89b-12d3-a456-426614174000"; + assertTrue(noPortValidator.isValid(validUrl, mockContext)); } @Test - void isValidUnreachable() { - CallableEndpointValidator callableEndpointValidator = createValidatorWithMockHttp((Interceptor.Chain chain) -> { - throw new RuntimeException("x"); - }); - - assertFalse(callableEndpointValidator.isValid("http://localhost:9999/my/callback/resource", null)); + void isValidReturnsFalseForInvalidCaseId() { + String invalidUrl = "http://localhost:8080/api/stitching-complete-callback/123456789012345/asyncStitchingComplete/123e4567-e89b-12d3-a456-426614174000"; + assertFalse(validator.isValid(invalidUrl, mockContext)); } - @Test - void isValidHttpUrlWithoutExplicitPortUsesDefaultPort() { - CallableEndpointValidator callableEndpointValidator = - createValidatorWithMockHttp((Interceptor.Chain chain) -> { - String requestedUrl = chain.request().url().toString(); - assertEquals("http://somehost.com/", requestedUrl); - return new Response.Builder() - .body(ResponseBody.create("", MediaType.parse("plain/text"))) - .request(chain.request()) - .message("") - .code(200) - .protocol(Protocol.HTTP_2) - .build(); - }); - - assertTrue(callableEndpointValidator.isValid("http://somehost.com/some/path", null)); + void isValidReturnsFalseForInvalidTriggerId() { + String invalidUrl = "http://localhost:8080/api/stitching-complete-callback/1234567890123456/wrongTriggerId/123e4567-e89b-12d3-a456-426614174000"; + assertFalse(validator.isValid(invalidUrl, mockContext)); } - private CallableEndpointValidator createValidatorWithMockHttp(Interceptor interceptor) { - OkHttpClient http = new OkHttpClient - .Builder() - .addInterceptor(interceptor) - .build(); - - return new CallableEndpointValidator(http); + @Test + void isValidReturnsFalseForInvalidBundleId() { + String invalidUrl = "http://localhost:8080/api/stitching-complete-callback/1234567890123456/asyncStitchingComplete/not-a-uuid"; + assertFalse(validator.isValid(invalidUrl, mockContext)); } -} + @Test + void isValidReturnsFalseForWrongHostOrScheme() { + String invalidUrl = "https://wrong-host:8080/api/stitching-complete-callback/1234567890123456/asyncStitchingComplete/123e4567-e89b-12d3-a456-426614174000"; + assertFalse(validator.isValid(invalidUrl, mockContext)); + } + @Test + void isValidReturnsFalseForAppendedPaths() { + String invalidUrl = "http://localhost:8080/api/stitching-complete-callback/1234567890123456/asyncStitchingComplete/123e4567-e89b-12d3-a456-426614174000/extra"; + assertFalse(validator.isValid(invalidUrl, mockContext)); + } + @Test + void isValidReturnsFalseForNullOrBlank() { + assertFalse(validator.isValid(null, mockContext)); + assertFalse(validator.isValid("", mockContext)); + assertFalse(validator.isValid(" ", mockContext)); + } +} \ No newline at end of file From 2d1288ac161bb4d3dda972c37ea225bafd950925 Mon Sep 17 00:00:00 2001 From: hmcts-jenkins-d-to-i <62423932+hmcts-jenkins-d-to-i[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 10:24:16 +0000 Subject: [PATCH 2/8] Bumping chart version/ fixing aliases --- charts/em-stitching/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/em-stitching/Chart.yaml b/charts/em-stitching/Chart.yaml index 10774658d..de6018ca5 100644 --- a/charts/em-stitching/Chart.yaml +++ b/charts/em-stitching/Chart.yaml @@ -1,6 +1,6 @@ name: em-stitching home: https://github.com/hmcts/rpa-em-stitching-api -version: 1.1.1 +version: 1.1.2 apiVersion: v2 description: Helm chart for the HMCTS EM Stitching API maintainers: From d42203feabe629eb906a21b8afaea50653263fd0 Mon Sep 17 00:00:00 2001 From: michaeljacob Date: Tue, 24 Mar 2026 16:00:21 +0000 Subject: [PATCH 3/8] fix tests --- .../functional/DocumentTaskScenarios.java | 17 ++++++++--- .../SecureDocumentTaskScenarios.java | 16 ++++++++-- .../em/stitching/testutil/TestUtil.java | 29 ++++++++++++++++--- 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java index b66273502..f22fbcdd1 100644 --- a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java @@ -10,6 +10,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; +import uk.gov.hmcts.reform.ccd.client.model.CaseDetails; import uk.gov.hmcts.reform.em.stitching.domain.enumeration.TaskState; import uk.gov.hmcts.reform.em.stitching.service.dto.BundleDTO; import uk.gov.hmcts.reform.em.stitching.service.dto.CallbackDto; @@ -272,7 +273,12 @@ void testStitchDocumentsWithSortIndices() throws IOException, InterruptedExcepti @Test void testPostBundleStitchWithCallback() throws IOException, InterruptedException { - String validCallbackUrl = testUtil.getValidCallbackUrl(); + String bundleId = java.util.UUID.randomUUID().toString(); + + CaseDetails caseDetails = testUtil.createCaseWithBundle(bundleId); + String realCaseId = String.valueOf(caseDetails.getId()); + + String validCallbackUrl = testUtil.getValidCallbackUrl(realCaseId, bundleId); BundleDTO bundle = testUtil.getTestBundle(); DocumentTaskDTO documentTask = new DocumentTaskDTO(); @@ -280,7 +286,6 @@ void testPostBundleStitchWithCallback() throws IOException, InterruptedException CallbackDto callback = new CallbackDto(); callback.setCallbackUrl(validCallbackUrl); - documentTask.setCallback(callback); Response createTaskResponse = @@ -290,7 +295,6 @@ void testPostBundleStitchWithCallback() throws IOException, InterruptedException .post(END_POINT); assertEquals(201, createTaskResponse.getStatusCode()); - assertEquals(validCallbackUrl, createTaskResponse.getBody().jsonPath().getString("callback.callbackUrl")); String taskUrl = END_POINT + "/" + createTaskResponse.getBody().jsonPath().getString("id"); @@ -299,7 +303,12 @@ void testPostBundleStitchWithCallback() throws IOException, InterruptedException @Test void testPostBundleStitchWithCallbackForFailure() throws IOException { - String validCallbackUrl = testUtil.getValidCallbackUrl(); + String bundleId = java.util.UUID.randomUUID().toString(); + + CaseDetails caseDetails = testUtil.createCaseWithBundle(bundleId); + String realCaseId = String.valueOf(caseDetails.getId()); + + String validCallbackUrl = testUtil.getValidCallbackUrl(realCaseId, bundleId); CallbackDto callback = new CallbackDto(); callback.setCallbackUrl(validCallbackUrl); diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java index 87f8d135d..b1d4586d0 100644 --- a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import uk.gov.hmcts.reform.ccd.client.model.CaseDetails; import uk.gov.hmcts.reform.em.stitching.domain.enumeration.TaskState; import uk.gov.hmcts.reform.em.stitching.service.dto.BundleDTO; import uk.gov.hmcts.reform.em.stitching.service.dto.CallbackDto; @@ -265,14 +266,18 @@ void testStitchDocumentsWithSortIndices() throws IOException, InterruptedExcepti @Test void testPostBundleStitchWithCallback() throws IOException, InterruptedException { - String validCallbackUrl = testUtil.getValidCallbackUrl(); + String bundleId = java.util.UUID.randomUUID().toString(); + + CaseDetails caseDetails = testUtil.createCaseWithBundle(bundleId); + String realCaseId = String.valueOf(caseDetails.getId()); + + String validCallbackUrl = testUtil.getValidCallbackUrl(realCaseId, bundleId); BundleDTO bundle = testUtil.getCdamTestBundle(); documentTask.setBundle(bundle); CallbackDto callback = new CallbackDto(); callback.setCallbackUrl(validCallbackUrl); - documentTask.setCallback(callback); Response createTaskResponse = @@ -290,7 +295,12 @@ void testPostBundleStitchWithCallback() throws IOException, InterruptedException @Test void testPostBundleStitchWithCallbackForFailure() throws IOException { - String validCallbackUrl = testUtil.getValidCallbackUrl(); + String bundleId = java.util.UUID.randomUUID().toString(); + + CaseDetails caseDetails = testUtil.createCaseWithBundle(bundleId); + String realCaseId = String.valueOf(caseDetails.getId()); + + String validCallbackUrl = testUtil.getValidCallbackUrl(realCaseId, bundleId); CallbackDto callback = new CallbackDto(); callback.setCallbackUrl(validCallbackUrl); diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/TestUtil.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/TestUtil.java index d5d7da793..7d57ae1f3 100644 --- a/src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/TestUtil.java +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/testutil/TestUtil.java @@ -888,10 +888,31 @@ public BundleDTO getCdamTestBundleWithSortedDocuments() throws JsonProcessingExc return bundle; } - public String getValidCallbackUrl() { + public String getValidCallbackUrl(String caseId, String bundleId) { String portStr = (callbackPort <= 0) ? "" : ":" + callbackPort; - return String.format("%s://%s%s/api/stitching-complete-callback" - + "/1234567890123456/asyncStitchingComplete/123e4567-e89b-12d3-a456-426614174000", - callbackScheme, callbackHost, portStr); + return String.format("%s://%s%s/api/stitching-complete-callback/%s/asyncStitchingComplete/%s", + callbackScheme, callbackHost, portStr, caseId, bundleId); + } + + public CaseDetails createCaseWithBundle(String bundleId) throws JsonProcessingException { + String payload = String.format(""" + { + "caseTitle": "Callback Test Case", + "caseDocuments": [], + "caseBundles":[ + { + "id": "%s", + "value": { + "id": "%s", + "title": "Callback Bundle", + "description": "Bundle for callback testing" + } + } + ] + }""", bundleId, bundleId); + + return ccdDataHelper.createCase( + STITCHING_TEST_USER_EMAIL, JURISDICTION, getEnvCcdCaseTypeId(), "createCase", + objectMapper.readTree(payload)); } } \ No newline at end of file From 901f9d6ce7ce761ead41bcdef286b779897bd839 Mon Sep 17 00:00:00 2001 From: michaeljacob Date: Tue, 24 Mar 2026 16:22:43 +0000 Subject: [PATCH 4/8] add jenkins var --- Jenkinsfile_CNP | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfile_CNP b/Jenkinsfile_CNP index b0a91a330..46aaf0775 100644 --- a/Jenkinsfile_CNP +++ b/Jenkinsfile_CNP @@ -48,6 +48,7 @@ env.CCD_DEF_API = 'http://ccd-definition-store-api-aat.service.core-compute-aat. env.CCD_DATA_API = 'http://ccd-data-store-api-aat.service.core-compute-aat.internal' env.CDAM_URL = 'http://ccd-case-document-am-api-aat.service.core-compute-aat.internal' env.DOCMOSIS_ENDPOINT = 'https://docmosis.aat.platform.hmcts.net/rs/convert' +env.CALLBACK_DOMAIN= 'em-ccd-orchestrator-aat.service.core-compute-aat.internal' def vaultOverrides = [ 'preview' : 'aat', From eb4a699685ec1478693dbd4408c19eb3557e5a45 Mon Sep 17 00:00:00 2001 From: michaeljacob Date: Tue, 24 Mar 2026 17:15:49 +0000 Subject: [PATCH 5/8] change port --- charts/em-stitching/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/em-stitching/values.yaml b/charts/em-stitching/values.yaml index 3b41c2406..37844df40 100644 --- a/charts/em-stitching/values.yaml +++ b/charts/em-stitching/values.yaml @@ -70,4 +70,4 @@ java: HTTP_CLIENT_SOCKET_TIMEOUT: 60000 APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL: INFO CALLBACK_HTTP_SCHEME: http - CALLBACK_HTTP_HOST_PORT: 8080 + CALLBACK_HTTP_HOST_PORT: 80 From 836ae61a8ae68ba3c219d134d3746aa932f4c130 Mon Sep 17 00:00:00 2001 From: michaeljacob Date: Wed, 25 Mar 2026 10:11:48 +0000 Subject: [PATCH 6/8] change port --- Jenkinsfile_CNP | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfile_CNP b/Jenkinsfile_CNP index 46aaf0775..9c2171144 100644 --- a/Jenkinsfile_CNP +++ b/Jenkinsfile_CNP @@ -49,6 +49,7 @@ env.CCD_DATA_API = 'http://ccd-data-store-api-aat.service.core-compute-aat.inter env.CDAM_URL = 'http://ccd-case-document-am-api-aat.service.core-compute-aat.internal' env.DOCMOSIS_ENDPOINT = 'https://docmosis.aat.platform.hmcts.net/rs/convert' env.CALLBACK_DOMAIN= 'em-ccd-orchestrator-aat.service.core-compute-aat.internal' +env.CALLBACK_HTTP_HOST_PORT= 80 def vaultOverrides = [ 'preview' : 'aat', From 1492934f5d4d11008048bc4a30f0480fc68989f6 Mon Sep 17 00:00:00 2001 From: michaeljacob Date: Wed, 25 Mar 2026 10:58:12 +0000 Subject: [PATCH 7/8] clean --- .../em/stitching/functional/DocumentTaskScenarios.java | 7 ++++--- .../stitching/functional/SecureDocumentTaskScenarios.java | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java index f22fbcdd1..607291892 100644 --- a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.IOException; import java.time.Instant; +import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -273,7 +274,7 @@ void testStitchDocumentsWithSortIndices() throws IOException, InterruptedExcepti @Test void testPostBundleStitchWithCallback() throws IOException, InterruptedException { - String bundleId = java.util.UUID.randomUUID().toString(); + String bundleId = UUID.randomUUID().toString(); CaseDetails caseDetails = testUtil.createCaseWithBundle(bundleId); String realCaseId = String.valueOf(caseDetails.getId()); @@ -303,7 +304,7 @@ void testPostBundleStitchWithCallback() throws IOException, InterruptedException @Test void testPostBundleStitchWithCallbackForFailure() throws IOException { - String bundleId = java.util.UUID.randomUUID().toString(); + String bundleId = UUID.randomUUID().toString(); CaseDetails caseDetails = testUtil.createCaseWithBundle(bundleId); String realCaseId = String.valueOf(caseDetails.getId()); @@ -346,7 +347,7 @@ void testPostBundleStitchWithInvalidCallbackUrl() throws IOException { documentTask.setBundle(bundle); CallbackDto callback = new CallbackDto(); - callback.setCallbackUrl("http://invalid-format-domain.com/wrong/path"); + callback.setCallbackUrl("https://postman-echo.com/post"); documentTask.setCallback(callback); diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java index b1d4586d0..e51403e1a 100644 --- a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.IOException; import java.time.Instant; +import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -266,7 +267,7 @@ void testStitchDocumentsWithSortIndices() throws IOException, InterruptedExcepti @Test void testPostBundleStitchWithCallback() throws IOException, InterruptedException { - String bundleId = java.util.UUID.randomUUID().toString(); + String bundleId = UUID.randomUUID().toString(); CaseDetails caseDetails = testUtil.createCaseWithBundle(bundleId); String realCaseId = String.valueOf(caseDetails.getId()); @@ -295,7 +296,7 @@ void testPostBundleStitchWithCallback() throws IOException, InterruptedException @Test void testPostBundleStitchWithCallbackForFailure() throws IOException { - String bundleId = java.util.UUID.randomUUID().toString(); + String bundleId = UUID.randomUUID().toString(); CaseDetails caseDetails = testUtil.createCaseWithBundle(bundleId); String realCaseId = String.valueOf(caseDetails.getId()); @@ -336,7 +337,7 @@ void testPostBundleStitchWithInvalidCallbackUrl() throws IOException { documentTask.setBundle(bundle); CallbackDto callback = new CallbackDto(); - callback.setCallbackUrl("http://invalid-format-domain.com/wrong/path"); + callback.setCallbackUrl("https://postman-echo.com/post"); documentTask.setCallback(callback); From 1d320581f9fb762ab05272f682455883669179a0 Mon Sep 17 00:00:00 2001 From: michaeljacob Date: Wed, 25 Mar 2026 11:29:44 +0000 Subject: [PATCH 8/8] clean --- .../uk/gov/hmcts/reform/em/stitching/functional/BaseTest.java | 3 +++ .../reform/em/stitching/functional/DocumentTaskScenarios.java | 3 --- .../em/stitching/functional/SecureDocumentTaskScenarios.java | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/BaseTest.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/BaseTest.java index 2d19e696d..64248e3b5 100644 --- a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/BaseTest.java +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/BaseTest.java @@ -6,7 +6,9 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; import org.springframework.test.context.TestPropertySource; +import uk.gov.hmcts.reform.em.stitching.testutil.CallbackMockConfig; import uk.gov.hmcts.reform.em.stitching.testutil.TestUtil; import uk.gov.hmcts.reform.em.test.retry.RetryExtension; @@ -15,6 +17,7 @@ @ExtendWith(SerenityJUnit5Extension.class) @WithTags({@WithTag("testType:Functional")}) @SuppressWarnings("java:S5960") +@Import(CallbackMockConfig.class) public abstract class BaseTest { protected final TestUtil testUtil; diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java index 607291892..aa846c6dd 100644 --- a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/DocumentTaskScenarios.java @@ -9,13 +9,11 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; import uk.gov.hmcts.reform.ccd.client.model.CaseDetails; import uk.gov.hmcts.reform.em.stitching.domain.enumeration.TaskState; import uk.gov.hmcts.reform.em.stitching.service.dto.BundleDTO; import uk.gov.hmcts.reform.em.stitching.service.dto.CallbackDto; import uk.gov.hmcts.reform.em.stitching.service.dto.DocumentTaskDTO; -import uk.gov.hmcts.reform.em.stitching.testutil.CallbackMockConfig; import uk.gov.hmcts.reform.em.stitching.testutil.TestUtil; import java.io.File; @@ -29,7 +27,6 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static uk.gov.hmcts.reform.em.stitching.testutil.TestUtil.convertObjectToJsonBytes; -@Import(CallbackMockConfig.class) class DocumentTaskScenarios extends BaseTest { private RequestSpecification request; diff --git a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java index e51403e1a..f0cdaca5c 100644 --- a/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java +++ b/src/aat/java/uk/gov/hmcts/reform/em/stitching/functional/SecureDocumentTaskScenarios.java @@ -27,7 +27,6 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static uk.gov.hmcts.reform.em.stitching.testutil.TestUtil.convertObjectToJsonBytes; - public class SecureDocumentTaskScenarios extends BaseTest { private static final String API_DOCUMENT_TASKS = "/api/document-tasks";