Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4bd4122
DMP-5271 [API Validation] GET/transcriptions/{transcription_id}/docum…
karen-hedges Oct 14, 2025
9cc0fcc
DMP-5271 [API Validation] GET/transcriptions/{transcription_id}/docum…
karen-hedges Oct 14, 2025
4a90844
DMP-5271 [API Validation] GET/transcriptions/{transcription_id}/docum…
karen-hedges Oct 14, 2025
b83dd2f
DMP-5271 [API Validation] GET/transcriptions/{transcription_id}/docum…
karen-hedges Oct 14, 2025
f748967
Add open api validation test
stephenokeefe Oct 15, 2025
91f7e50
Merge remote-tracking branch 'origin/DMP-5271-AddApiValidationGetTran…
stephenokeefe Oct 15, 2025
7e0ad0c
Integration tests and Open API validation
stephenokeefe Oct 17, 2025
67e897d
Merge branch 'master' into DMP-5271-AddApiValidationGetTranscriptions…
stephenokeefe Oct 17, 2025
71529c7
Refactor
stephenokeefe Oct 22, 2025
72bbe10
Merge remote-tracking branch 'origin/DMP-5271-AddApiValidationGetTran…
stephenokeefe Oct 22, 2025
287d18b
Merge branch 'master' into DMP-5271-AddApiValidationGetTranscriptions…
stephenokeefe Oct 22, 2025
b6f1b48
Add test coverage
stephenokeefe Oct 22, 2025
4267773
Merge remote-tracking branch 'origin/DMP-5271-AddApiValidationGetTran…
stephenokeefe Oct 22, 2025
e22961f
Add test coverage
stephenokeefe Oct 23, 2025
361ee2b
Add not found tests
stephenokeefe Oct 23, 2025
78a187f
Merge branch 'master' into DMP-5271-AddApiValidationGetTranscriptions…
stephenokeefe Oct 24, 2025
fbbde1a
Remove whitespace
stephenokeefe Oct 24, 2025
3cdf6b3
Merge remote-tracking branch 'origin/DMP-5271-AddApiValidationGetTran…
stephenokeefe Oct 24, 2025
778f0e3
DMP-5271 [API Validation] GET/transcriptions/{transcription_id}/docum…
karen-hedges Oct 14, 2025
c3679ae
DMP-5271 [API Validation] GET/transcriptions/{transcription_id}/docum…
karen-hedges Oct 14, 2025
c580d52
DMP-5271 [API Validation] GET/transcriptions/{transcription_id}/docum…
karen-hedges Oct 14, 2025
99b9bd7
Add open api validation test
stephenokeefe Oct 15, 2025
8c5e490
DMP-5271 [API Validation] GET/transcriptions/{transcription_id}/docum…
karen-hedges Oct 14, 2025
cd0eefd
Integration tests and Open API validation
stephenokeefe Oct 17, 2025
c4fcf8f
Refactor
stephenokeefe Oct 22, 2025
f8a3e78
Add test coverage
stephenokeefe Oct 22, 2025
b2bd3ab
Add test coverage
stephenokeefe Oct 23, 2025
e7d0e7b
Add not found tests
stephenokeefe Oct 23, 2025
8c408b7
Remove whitespace
stephenokeefe Oct 24, 2025
966451f
Merge remote-tracking branch 'origin/DMP-5271-AddApiValidationGetTran…
stephenokeefe Oct 31, 2025
5a697f7
Adjust tests from master to account for new validation
stephenokeefe Oct 31, 2025
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
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,8 @@ dependencies {

testImplementation group: 'org.jeasy', name: 'easy-random-core', version: '5.0.0'

testImplementation 'com.atlassian.oai:swagger-request-validator-core:2.46.0'

compileJava.dependsOn = openApiGenerateTaskList
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import static uk.gov.hmcts.darts.common.enums.SecurityRoleEnum.JUDICIARY;
import static uk.gov.hmcts.darts.common.enums.SecurityRoleEnum.REQUESTER;
import static uk.gov.hmcts.darts.hearings.exception.HearingApiError.HEARING_NOT_FOUND;
import static uk.gov.hmcts.darts.transcriptions.exception.TranscriptionApiError.TRANSCRIPTION_NOT_FOUND;
import static uk.gov.hmcts.darts.transcriptions.exception.TranscriptionApiError.BAD_REQUEST_TRANSCRIPTION_ID;

class AuthorisationImplTest extends IntegrationBase {

Expand Down Expand Up @@ -222,8 +222,8 @@ void authoriseByTranscriptionIdShouldThrowTranscriptionApiErrorTranscriptionNotF
-1L, Set.of(JUDICIARY))
);

assertEquals(TRANSCRIPTION_NOT_FOUND.getTitle(), exception.getMessage());
assertEquals(TRANSCRIPTION_NOT_FOUND, exception.getError());
assertEquals(BAD_REQUEST_TRANSCRIPTION_ID.getTitle(), exception.getMessage());
assertEquals(BAD_REQUEST_TRANSCRIPTION_ID, exception.getError());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import static uk.gov.hmcts.darts.audit.api.AuditActivity.DOWNLOAD_TRANSCRIPTION;
import static uk.gov.hmcts.darts.common.enums.ExternalLocationTypeEnum.UNSTRUCTURED;
import static uk.gov.hmcts.darts.common.enums.ObjectRecordStatusEnum.STORED;
import static uk.gov.hmcts.darts.test.common.TestUtils.getContentsFromFile;
import static uk.gov.hmcts.darts.transcriptions.enums.TranscriptionStatusEnum.APPROVED;
import static uk.gov.hmcts.darts.transcriptions.enums.TranscriptionStatusEnum.COMPLETE;
import static uk.gov.hmcts.darts.transcriptions.enums.TranscriptionStatusEnum.WITH_TRANSCRIBER;
Expand Down Expand Up @@ -163,7 +164,7 @@ void downloadTranscript_shouldReturn401Error_whenUserNotFound() throws Exception
}

@Test
void downloadTranscriptShouldReturnNotFoundError() throws Exception {
void downloadTranscript_ShouldReturnNotFoundError() throws Exception {
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(URL_TEMPLATE, transcriptionId)
.header(
"accept",
Expand All @@ -185,7 +186,7 @@ void downloadTranscriptShouldReturnNotFoundError() throws Exception {
}

@Test
void downloadTranscriptShouldReturnOkWithMicrosoftWordNew() throws Exception {
void downloadTranscript_ShouldReturnOk_WithMicrosoftWordNew() throws Exception {
final String fileName = "Test Document.docx";
final String fileType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
final int fileSize = 11_937;
Expand Down Expand Up @@ -257,7 +258,7 @@ void downloadTranscriptShouldReturnOkWithMicrosoftWordNew() throws Exception {

@Test
@SuppressWarnings("PMD.CloseResource")
void downloadTranscriptShouldReturnOkWithMicrosoftWordOld() throws Exception {
void downloadTranscript_ShouldReturnOk_WithMicrosoftWordOld() throws Exception {
final String fileName = "Test Document.doc";
final String fileType = "application/msword";
final int fileSize = 22_528;
Expand Down Expand Up @@ -319,4 +320,23 @@ void downloadTranscriptShouldReturnOkWithMicrosoftWordOld() throws Exception {
verifyNoMoreInteractions(mockAuditApi, mockDataManagementFacade, mockFileBasedDownloadResponseMetaData);
}

@Test
void downloadTranscript_ShouldReturnBadRequest_WhenNegativeTranscriptionIdUsed() throws Exception {
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/transcriptions/-123/document")
.header(
"accept",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
);
MvcResult mvcResult = mockMvc.perform(requestBuilder)
.andExpect(status().isBadRequest())
.andReturn();

String actualResponse = mvcResult.getResponse().getContentAsString();

String expectedResponse = getContentsFromFile("tests/transcriptions/transcription/expectedResponseBadRequest.json");
JSONAssert.assertEquals(expectedResponse, actualResponse, JSONCompareMode.NON_EXTENSIBLE);

verifyNoInteractions(mockAuditApi);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,33 @@ void getTranscriptionNotFoundWhenIsCurrentFalse() throws Exception {
}

@Test
void getTranscriptionNotFound() throws Exception {
void getTranscription_ShouldReturnBadRequest_WhenNegativeTranscriptionId() throws Exception {
MockHttpServletRequestBuilder requestBuilder = get(ENDPOINT_URL_TRANSCRIPTION, -999);
MvcResult response = mockMvc.perform(requestBuilder).andExpect(status().isBadRequest()).andReturn();
String actualResponse = response.getResponse().getContentAsString();
String expectedResponse = getContentsFromFile("tests/transcriptions/transcription/expectedResponseBadRequest.json");
JSONAssert.assertEquals(expectedResponse, actualResponse, JSONCompareMode.NON_EXTENSIBLE);
}

@Test
void getTranscription_ShouldReturnNotFound_WhenTranscriptDoesNotExist() throws Exception {
TranscriptionEntity transcriptionEntity = transactionalUtil.executeInTransaction(() -> {
HearingEntity hearingEntity = dartsDatabase.getHearingRepository().findAll().getFirst();
TranscriptionEntity transcription = dartsDatabase.getTranscriptionStub().createTranscription(hearingEntity);
transcription.setStartTime(SOME_DATE_TIME);
transcription.setEndTime(SOME_DATE_TIME);
transcription = dartsDatabase.save(transcription);
UserAccountEntity userAccount = dartsDatabase.getUserAccountRepository().findById(transcription.getCreatedById()).orElseThrow();

addTranscriptionWorkflow(transcription, userAccount, "comment1", TranscriptionStatusEnum.REQUESTED);
addTranscriptionWorkflow(transcription, userAccount, "comment2", TranscriptionStatusEnum.APPROVED);
transcription.getCourtroom().getCourthouse().getCourthouseName();
transcription.getRequestedBy().getUserName();
return transcription;
});
dartsDatabase.updateCreatedBy(transcriptionEntity, OffsetDateTime.of(2023, 6, 20, 10, 0, 0, 0, ZoneOffset.UTC));

MockHttpServletRequestBuilder requestBuilder = get(ENDPOINT_URL_TRANSCRIPTION, transcriptionEntity.getId() + 1);
MvcResult response = mockMvc.perform(requestBuilder).andExpect(status().isNotFound()).andReturn();
String actualResponse = response.getResponse().getContentAsString();
String expectedResponse = getContentsFromFile("tests/transcriptions/transcription/expectedResponseNotFound.json");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static uk.gov.hmcts.darts.audit.api.AuditActivity.UNFULFILLED_TRANSCRIPTION;
import static uk.gov.hmcts.darts.test.common.TestUtils.getContentsFromFile;
import static uk.gov.hmcts.darts.transcriptions.enums.TranscriptionStatusEnum.CLOSED;
import static uk.gov.hmcts.darts.transcriptions.enums.TranscriptionStatusEnum.UNFULFILLED;
import static uk.gov.hmcts.darts.transcriptions.enums.TranscriptionStatusEnum.WITH_TRANSCRIBER;
Expand Down Expand Up @@ -168,12 +169,11 @@ void updateTranscription_ShouldReturnTranscriptionUnprocessableEntityError_Where
}

@Test
void updateTranscription_ShouldReturnTranscriptionNotFoundError() throws Exception {
void updateTranscription_ShouldReturnTranscriptionNotFoundError_WhenTranscriptionDoesNotExist() throws Exception {
UpdateTranscriptionRequest updateTranscription = new UpdateTranscriptionRequest();
updateTranscription.setTranscriptionStatusId(UNFULFILLED.getId());

MockHttpServletRequestBuilder requestBuilder = patch(URI.create(
String.format("/transcriptions/%d", -1)))
String.format("/transcriptions/%d", transcriptionId + 1)))
.header("Content-Type", "application/json")
.content(objectMapper.writeValueAsString(updateTranscription));
MvcResult mvcResult = mockMvc.perform(requestBuilder)
Expand All @@ -189,6 +189,27 @@ void updateTranscription_ShouldReturnTranscriptionNotFoundError() throws Excepti
verifyNoInteractions(mockAuditApi);
}

@Test
void updateTranscription_ShouldReturnTranscriptionBadRequestError_WhenNegativeTranscriptionIdUsed() throws Exception {
UpdateTranscriptionRequest updateTranscription = new UpdateTranscriptionRequest();
updateTranscription.setTranscriptionStatusId(UNFULFILLED.getId());

MockHttpServletRequestBuilder requestBuilder = patch(URI.create(
String.format("/transcriptions/%d", -1)))
.header("Content-Type", "application/json")
.content(objectMapper.writeValueAsString(updateTranscription));
MvcResult mvcResult = mockMvc.perform(requestBuilder)
.andExpect(status().isBadRequest())
.andReturn();

String actualJson = mvcResult.getResponse().getContentAsString();
String expectedJson = getContentsFromFile("tests/transcriptions/transcription/expectedResponseBadRequest.json");

JSONAssert.assertEquals(expectedJson, actualJson, JSONCompareMode.NON_EXTENSIBLE);
verifyNoInteractions(notificationApi);
verifyNoInteractions(mockAuditApi);
}

@Test
void updateTranscription_ShouldReturnTranscriptionWorkflowActionInvalidError() throws Exception {
UpdateTranscriptionRequest updateTranscription = new UpdateTranscriptionRequest();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static uk.gov.hmcts.darts.audit.api.AuditActivity.ACCEPT_TRANSCRIPTION;
import static uk.gov.hmcts.darts.test.common.TestUtils.getContentsFromFile;
import static uk.gov.hmcts.darts.transcriptions.enums.TranscriptionStatusEnum.APPROVED;
import static uk.gov.hmcts.darts.transcriptions.enums.TranscriptionStatusEnum.COMPLETE;
import static uk.gov.hmcts.darts.transcriptions.enums.TranscriptionStatusEnum.WITH_TRANSCRIBER;
Expand Down Expand Up @@ -133,22 +134,41 @@ void updateTranscriptionWithTranscriberWithoutComment() throws Exception {
}

@Test
void updateTranscriptionShouldReturnTranscriptionNotFoundError() throws Exception {
void updateTranscription_ShouldReturnBadRequest_WhenInvalidTranscriptionId() throws Exception {
UpdateTranscriptionRequest updateTranscription = new UpdateTranscriptionRequest();
updateTranscription.setTranscriptionStatusId(WITH_TRANSCRIBER.getId());

MockHttpServletRequestBuilder requestBuilder = patch(URI.create(
String.format("/transcriptions/%d", -1)))
.header("Content-Type", "application/json")
.content(objectMapper.writeValueAsString(updateTranscription));
MvcResult mvcResult = mockMvc.perform(requestBuilder)
.andExpect(status().isBadRequest())
.andReturn();

String actualJson = mvcResult.getResponse().getContentAsString();
String expectedJson = getContentsFromFile("tests/transcriptions/transcription/expectedResponseBadRequest.json");
JSONAssert.assertEquals(expectedJson, actualJson, JSONCompareMode.NON_EXTENSIBLE);

verifyNoInteractions(mockAuditApi);
}

@Test
void updateTranscription_ShouldReturnNotFound_WhenTranscriptionDoesNotExist() throws Exception {
UpdateTranscriptionRequest updateTranscription = new UpdateTranscriptionRequest();
updateTranscription.setTranscriptionStatusId(WITH_TRANSCRIBER.getId());

MockHttpServletRequestBuilder requestBuilder = patch(URI.create(
String.format("/transcriptions/%d", transcriptionId + 1)))
.header("Content-Type", "application/json")
.content(objectMapper.writeValueAsString(updateTranscription));
MvcResult mvcResult = mockMvc.perform(requestBuilder)
.andExpect(status().isNotFound())
.andReturn();

String actualJson = mvcResult.getResponse().getContentAsString();
String expectedJson = """
{"type":"TRANSCRIPTION_101","title":"The requested transcription cannot be found","status":404}
""";
String expectedJson = getContentsFromFile("tests/transcriptions/transcription/expectedResponseNotFound.json");

JSONAssert.assertEquals(expectedJson, actualJson, JSONCompareMode.NON_EXTENSIBLE);

verifyNoInteractions(mockAuditApi);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"type":"TRANSCRIPTION_124","title":"Invalid transcription id","status":400}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.coyote.BadRequestException;
import org.springframework.stereotype.Component;
import uk.gov.hmcts.darts.audio.entity.MediaRequestEntity;
import uk.gov.hmcts.darts.authorisation.api.AuthorisationApi;
Expand All @@ -22,6 +23,8 @@
import uk.gov.hmcts.darts.common.repository.MediaRequestRepository;
import uk.gov.hmcts.darts.common.repository.TranscriptionRepository;
import uk.gov.hmcts.darts.common.repository.TransformedMediaRepository;
import uk.gov.hmcts.darts.util.DataUtil;
import uk.gov.hmcts.darts.util.ValidationConstants;

import java.util.Collections;
import java.util.List;
Expand All @@ -36,6 +39,7 @@
import static uk.gov.hmcts.darts.audio.exception.AudioRequestsApiError.TRANSFORMED_MEDIA_NOT_FOUND;
import static uk.gov.hmcts.darts.cases.exception.CaseApiError.CASE_NOT_FOUND;
import static uk.gov.hmcts.darts.hearings.exception.HearingApiError.HEARING_NOT_FOUND;
import static uk.gov.hmcts.darts.transcriptions.exception.TranscriptionApiError.BAD_REQUEST_TRANSCRIPTION_ID;
import static uk.gov.hmcts.darts.transcriptions.exception.TranscriptionApiError.TRANSCRIPTION_NOT_FOUND;

@Component
Expand Down Expand Up @@ -115,6 +119,9 @@ public void authoriseByMediaId(Long mediaId, Set<SecurityRoleEnum> securityRoles
@SuppressWarnings({"PMD.ExceptionAsFlowControl"})
public void authoriseByTranscriptionId(Long transcriptionId, Set<SecurityRoleEnum> securityRoles) {
try {
if (!DataUtil.isWithinBounds(transcriptionId, 1L, ValidationConstants.MaxValues.MAX_LONG_VALUE)) {
throw new BadRequestException();
}
final List<CourthouseEntity> courthouses = getCourthousesFromTranscription(transcriptionId);
if (CollectionUtils.isEmpty(courthouses)) {
throw new EntityNotFoundException();
Expand All @@ -123,6 +130,9 @@ public void authoriseByTranscriptionId(Long transcriptionId, Set<SecurityRoleEnu
} catch (EntityNotFoundException ex) {
log.error("Unable to find Transcription-Courtroom-Courthouse for checkAuthorisation. TranscriptionId={}", transcriptionId, ex);
throw new DartsApiException(TRANSCRIPTION_NOT_FOUND, ex);
} catch (BadRequestException ex) {
log.error("TranscriptionId is out of accepted range. TranscriptionId={}", transcriptionId, ex);
throw new DartsApiException(BAD_REQUEST_TRANSCRIPTION_ID, ex);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,12 @@ public enum TranscriptionApiError implements DartsApiError {
TOO_MANY_RESULTS(
TranscriptionsErrorCode.TOO_MANY_RESULTS.getValue(),
HttpStatus.UNPROCESSABLE_ENTITY,
TranscriptionsTitleErrors.TOO_MANY_RESULTS.getValue());
TranscriptionsTitleErrors.TOO_MANY_RESULTS.getValue()
),
BAD_REQUEST_TRANSCRIPTION_ID(
TranscriptionsErrorCode.BAD_REQUEST_TRANSCRIPTION_ID.getValue(),
HttpStatus.BAD_REQUEST,
TranscriptionsTitleErrors.BAD_REQUEST_TRANSCRIPTION_ID.getValue());


private static final String ERROR_TYPE_PREFIX = "TRANSCRIPTION";
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/uk/gov/hmcts/darts/util/DataUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,15 @@ public static boolean toBoolean(Boolean value) {
public static boolean toBoolean(Boolean value, boolean defaultValue) {
return Optional.ofNullable(value).orElse(defaultValue);
}

public static boolean isWithinBounds(Long value, Long min, Long max) {
if (value == null) {
return false;
}
if (min != null && value < min) {
return false;
}
return max == null || value <= max;
}

}
12 changes: 12 additions & 0 deletions src/main/java/uk/gov/hmcts/darts/util/ValidationConstants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package uk.gov.hmcts.darts.util;

import lombok.experimental.UtilityClass;

@UtilityClass
@SuppressWarnings({"HideUtilityClassConstructor", "java:S1118", "PMD.MissingStaticMethodInNonInstantiatableClass"})
public class ValidationConstants {

public static class MaxValues {
public static final Long MAX_LONG_VALUE = 9_223_372_036_854_775_807L;
}
}
8 changes: 4 additions & 4 deletions src/main/resources/openapi/annotations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ paths:
annotation:
$ref: '#/components/schemas/annotation'
responses:
201:
'201':
description: CREATED
content:
application/json:
Expand All @@ -43,7 +43,7 @@ paths:
content:
application/json+problem:
schema:
$ref: './problem.yaml'
$ref: './problem.yaml'
'400':
description: Bad Request Error
content:
Expand Down Expand Up @@ -78,7 +78,7 @@ paths:
content:
application/json+problem:
schema:
$ref: './problem.yaml'
$ref: './problem.yaml'
'400':
description: Bad Request Error
content:
Expand Down Expand Up @@ -132,7 +132,7 @@ paths:
content:
application/json+problem:
schema:
$ref: './problem.yaml'
$ref: './problem.yaml'
'400':
description: Bad Request Error
content:
Expand Down
Loading