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
15 changes: 15 additions & 0 deletions src/integrationTest/java/uk/gov/hmcts/cp/config/TestConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package uk.gov.hmcts.cp.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import uk.gov.hmcts.cp.repositories.CourtScheduleRepository;
import uk.gov.hmcts.cp.repositories.CourtScheduleRepositoryImpl;

import java.net.http.HttpClient;

import static org.mockito.Mockito.mock;

@Configuration
public class TestConfig {

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import uk.gov.hmcts.cp.config.TestConfig;
import uk.gov.hmcts.cp.repositories.CourtScheduleRepository;

import java.util.UUID;

Expand All @@ -24,12 +28,23 @@
@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
@Import(TestConfig.class)
class CourtScheduleControllerIT {
private static final Logger log = LoggerFactory.getLogger(CourtScheduleControllerIT.class);

@Autowired
private MockMvc mockMvc;

@Autowired
@Qualifier("inMemoryCourtScheduleRepositoryImpl")
private CourtScheduleRepository courtScheduleRepository;

/*@BeforeEach
void setUp() {
inMemoryCaseUrnMapper.clearAllMappings();
inMemoryCaseUrnMapper.saveCaseUrnMapping("test-case-urn", "test-case-id");
}
*/
@Test
void shouldReturnOkWhenValidUrnIsProvided() throws Exception {
String caseUrn = "test-case-urn";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package uk.gov.hmcts.cp.repositories;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import uk.gov.hmcts.cp.openapi.model.CourtSchedule;
import uk.gov.hmcts.cp.openapi.model.CourtScheduleResponse;
import uk.gov.hmcts.cp.openapi.model.CourtSitting;
import uk.gov.hmcts.cp.openapi.model.Hearing;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Primary
public class CourtScheduleRepositoryImpl implements CourtScheduleRepository {
private static final Logger LOG = LoggerFactory.getLogger(CourtScheduleRepositoryImpl.class);

private final Map<String, CourtScheduleResponse> courtScheduleResponseMap = new ConcurrentHashMap<>();


public void saveCourtSchedule(final String caseUrn, final CourtScheduleResponse courtScheduleResponse) {
courtScheduleResponseMap.put(caseUrn, courtScheduleResponse);
}

public CourtScheduleResponse getCourtScheduleByCaseId(final String caseUrn) {
if (!courtScheduleResponseMap.containsKey(caseUrn)) {
saveCourtSchedule(caseUrn, createCourtScheduleResponse());
}
return courtScheduleResponseMap.get(caseUrn);
}

public void clearAll() {
courtScheduleResponseMap.clear();
}

private CourtScheduleResponse createCourtScheduleResponse() {

String res = getHearingData();

Check warning on line 50 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Local variable 'res' could be declared final

A local variable assigned only once can be declared final. LocalVariableCouldBeFinal (Priority: 3, Ruleset: Code Style) https://docs.pmd-code.org/snapshot/pmd_rules_java_codestyle.html#localvariablecouldbefinal
LOG.info("infunction createCourtScheduleResponse Response Body: {} " + res);

Check failure on line 51 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Logger calls should be surrounded by log level guards.

Whenever using a log level, one should check if it is actually enabled, or otherwise skip the associate String creation and manipulation, as well as any method calls. An alternative to checking the log level are substituting parameters, formatters or lazy logging with lambdas. The available alternatives depend on the actual logging framework. GuardLogStatement (Priority: 2, Ruleset: Best Practices) https://docs.pmd-code.org/snapshot/pmd_rules_java_bestpractices.html#guardlogstatement


final OffsetDateTime sittingStartTime = OffsetDateTime.now(ZoneOffset.UTC)
.truncatedTo(ChronoUnit.SECONDS);

final Hearing hearing = Hearing.builder()
.hearingId(UUID.randomUUID().toString())
.listNote("Requires interpreter")
.hearingDescription("Sentencing for theft case")
.hearingType("Trial")
.courtSittings(List.of(
CourtSitting.builder()
.courtHouse("Central Criminal Court")
.sittingStart(sittingStartTime)
.sittingEnd(sittingStartTime.plusMinutes(60))
.judiciaryId(UUID.randomUUID().toString())
.build())
).build();

return CourtScheduleResponse.builder()
.courtSchedule(List.of(
CourtSchedule.builder()
.hearings(List.of(hearing)
).build()
)
).build();
}

private String getHearingData(){
HttpResponse<String> response = null;

Check warning on line 81 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

The initializer for variable 'response' is never used (overwritten on line 95)

Reports assignments to variables that are never used before the variable is overwritten, or goes out of scope. Unused assignments are those for which 1. The variable is never read after the assignment, or 2. The assigned value is always overwritten by other assignments before the next read of the variable. The rule tracks assignements to fields of `this`, and static fields of the current class. This may cause some false positives in timing-sensitive concurrent code, which the rule cannot detect. The rule may be suppressed with the standard `@SuppressWarnings("unused")` tag. The rule subsumes {% rule "UnusedLocalVariable" %}, and {% rule "UnusedFormalParameter" %}. Those violations are filtered out by default, in case you already have enabled those rules, but may be enabled with the property `reportUnusedVariables`. Variables whose name starts with `ignored` or `unused` are filtered out, as is standard practice for exceptions. Limitations: * The rule currently cannot know which method calls throw exceptions, or which exceptions they throw. In the body of a try block, every method or constructor call is assumed to throw. This may cause false-negatives. The only other language construct that is assumed to throw is the `throw` statement, in particular, things like `assert` statements, or NullPointerExceptions on dereference are ignored. * The rule cannot resolve assignments across constructors, when they're called with the special `this(...)` syntax. This may cause false-negatives. Both of those limitations may be partly relaxed in PMD 7. UnusedAssignment (Priority: 3, Ruleset: Best Practices) https://docs.pmd-code.org/snapshot/pmd_rules_java_bestpractices.html#unusedassignment
try {
// Create HttpClient instance
HttpClient client = HttpClient.newHttpClient();

Check warning on line 84 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Local variable 'client' could be declared final

A local variable assigned only once can be declared final. LocalVariableCouldBeFinal (Priority: 3, Ruleset: Code Style) https://docs.pmd-code.org/snapshot/pmd_rules_java_codestyle.html#localvariablecouldbefinal

Check warning on line 84 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Ensure that resources like this HttpClient object are closed after use

Ensure that resources (like `java.sql.Connection`, `java.sql.Statement`, and `java.sql.ResultSet` objects and any subtype of `java.lang.AutoCloseable`) are always closed after use. Failing to do so might result in resource leaks. Note: It suffices to configure the super type, e.g. `java.lang.AutoCloseable`, so that this rule automatically triggers on any subtype (e.g. `java.io.FileInputStream`). Additionally specifying `java.sql.Connection` helps in detecting the types, if the type resolution / auxclasspath is not correctly setup. Note: Since PMD 6.16.0 the default value for the property `types` contains `java.lang.AutoCloseable` and detects now cases where the standard `java.io.*Stream` classes are involved. In order to restore the old behaviour, just remove "AutoCloseable" from the types. CloseResource (Priority: 3, Ruleset: Error Prone) https://docs.pmd-code.org/snapshot/pmd_rules_java_errorprone.html#closeresource

// Build the request
HttpRequest request = HttpRequest.newBuilder()

Check warning on line 87 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Local variable 'request' could be declared final

A local variable assigned only once can be declared final. LocalVariableCouldBeFinal (Priority: 3, Ruleset: Code Style) https://docs.pmd-code.org/snapshot/pmd_rules_java_codestyle.html#localvariablecouldbefinal
.uri(new URI("https://steccm64.ingress01.dev.nl.cjscp.org.uk/listing-query-api/query/api/rest/listing/hearings/allocated-and-unallocated?caseId=f552dee6-f092-415b-839c-5e5b5f46635e"))
.GET()
.header("Accept", "application/vnd.listing.search.hearings+json")
.header("CJSCPPUID", "d7c91866-646a-462c-9203-46678e8cddef")
.build();

// Send the request
response = client.send(request, HttpResponse.BodyHandlers.ofString());

// Check response status
if (response.statusCode() != HttpStatus.OK.value()) {
LOG.error("Failed to fetch hearing data. HTTP Status: {}", response.statusCode());

Check failure on line 99 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Logger calls should be surrounded by log level guards.

Whenever using a log level, one should check if it is actually enabled, or otherwise skip the associate String creation and manipulation, as well as any method calls. An alternative to checking the log level are substituting parameters, formatters or lazy logging with lambdas. The available alternatives depend on the actual logging framework. GuardLogStatement (Priority: 2, Ruleset: Best Practices) https://docs.pmd-code.org/snapshot/pmd_rules_java_bestpractices.html#guardlogstatement
return null;

Check warning on line 100 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

A method should have only one exit point, and that should be the last statement in the method

A method should have only one exit point, and that should be the last statement in the method. OnlyOneReturn (Priority: 3, Ruleset: Code Style) https://docs.pmd-code.org/snapshot/pmd_rules_java_codestyle.html#onlyonereturn
}

// Print response status and body
LOG.info("Response Code: {} " + response.statusCode());

Check failure on line 104 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Logger calls should be surrounded by log level guards.

Whenever using a log level, one should check if it is actually enabled, or otherwise skip the associate String creation and manipulation, as well as any method calls. An alternative to checking the log level are substituting parameters, formatters or lazy logging with lambdas. The available alternatives depend on the actual logging framework. GuardLogStatement (Priority: 2, Ruleset: Best Practices) https://docs.pmd-code.org/snapshot/pmd_rules_java_bestpractices.html#guardlogstatement
LOG.info("Response Body: {} " + response.body());

Check failure on line 105 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Logger calls should be surrounded by log level guards.

Whenever using a log level, one should check if it is actually enabled, or otherwise skip the associate String creation and manipulation, as well as any method calls. An alternative to checking the log level are substituting parameters, formatters or lazy logging with lambdas. The available alternatives depend on the actual logging framework. GuardLogStatement (Priority: 2, Ruleset: Best Practices) https://docs.pmd-code.org/snapshot/pmd_rules_java_bestpractices.html#guardlogstatement
return response.body();

Check warning on line 106 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

A method should have only one exit point, and that should be the last statement in the method

A method should have only one exit point, and that should be the last statement in the method. OnlyOneReturn (Priority: 3, Ruleset: Code Style) https://docs.pmd-code.org/snapshot/pmd_rules_java_codestyle.html#onlyonereturn
} catch (Exception e) {
LOG.error("Exception occurred while fetching hearing data: {}", e.getMessage(), e);

Check failure on line 108 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Logger calls should be surrounded by log level guards.

Whenever using a log level, one should check if it is actually enabled, or otherwise skip the associate String creation and manipulation, as well as any method calls. An alternative to checking the log level are substituting parameters, formatters or lazy logging with lambdas. The available alternatives depend on the actual logging framework. GuardLogStatement (Priority: 2, Ruleset: Best Practices) https://docs.pmd-code.org/snapshot/pmd_rules_java_bestpractices.html#guardlogstatement
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
Expand All @@ -18,6 +19,7 @@ public class CourtScheduleService {

private static final Logger LOG = LoggerFactory.getLogger(CourtScheduleService.class);

@Qualifier("courtScheduleRepositoryImpl")
private final CourtScheduleRepository courtScheduleRepository;

public CourtScheduleResponse getCourtScheduleByCaseId(final String caseId) throws ResponseStatusException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package uk.gov.hmcts.cp.repositories;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import uk.gov.hmcts.cp.openapi.model.CourtScheduleResponse;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;


class CourtScheduleRepositoryImplTest {

private CourtScheduleRepositoryImpl courtScheduleRepository;

@BeforeEach
void setUp() {
courtScheduleRepository = new CourtScheduleRepositoryImpl();
}

@Test
void getCourtScheduleByCaseId_shouldReturnNewResponseIfNotExists() {
String caseUrn = "test-case-urn";

CourtScheduleResponse response = courtScheduleRepository.getCourtScheduleByCaseId(caseUrn);

assertNotNull(response);
assertEquals(1, response.getCourtSchedule().size());
}

@Test
void getCourtScheduleByCaseId_shouldReturnExistingResponseIfExists() {
String caseUrn = "test-case-urn";
CourtScheduleResponse expectedResponse = CourtScheduleResponse.builder().build();

courtScheduleRepository.saveCourtSchedule(caseUrn, expectedResponse);
CourtScheduleResponse actualResponse = courtScheduleRepository.getCourtScheduleByCaseId(caseUrn);

assertNotNull(actualResponse);
assertEquals(expectedResponse, actualResponse);
}

@Test
void saveCourtSchedule_shouldStoreResponse() {
String caseUrn = "test-case-urn";
CourtScheduleResponse response = CourtScheduleResponse.builder().build();

courtScheduleRepository.saveCourtSchedule(caseUrn, response);
CourtScheduleResponse storedResponse = courtScheduleRepository.getCourtScheduleByCaseId(caseUrn);

assertNotNull(storedResponse);
assertEquals(response, storedResponse);
}
}
104 changes: 104 additions & 0 deletions src/test/resources/hearing.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"hearings": [
{
"id": "348a288e-6e0d-479c-bd14-f363580d2181",
"type": {
"id": "4a0e892d-c0c5-3c51-95b8-704d8c781776",
"description": "First hearing"
},
"endDate": "2025-07-21",
"allocated": true,
"judiciary": [],
"startDate": "2025-07-21",
"courtRoomId": "9e4932f7-97b2-3010-b942-ddd2624e4dd8",
"hearingDays": [
{
"endTime": "2025-07-21T15:20:00.000Z",
"sequence": 0,
"startTime": "2025-07-21T15:00:00.000Z",
"courtRoomId": "9e4932f7-97b2-3010-b942-ddd2624e4dd8",
"hearingDate": "2025-07-21",
"courtCentreId": "f8254db1-1683-483e-afb3-b87fde5a0a26",
"durationMinutes": 20
}
],
"listedCases": [
{
"id": "f552dee6-f092-415b-839c-5e5b5f46635e",
"markers": [],
"defendants": [
{
"id": "4e7853ab-67f8-4ebe-9a31-dc1ed271ebb9",
"address": {
"address1": "Add1",
"postcode": "CB3 0GU"
},
"isYouth": false,
"lastName": "Test2",
"offences": [
{
"id": "922cf1fd-9ef1-415d-809f-5a871ec24ec8",
"count": 0,
"startDate": "2025-07-10",
"orderIndex": 1,
"offenceCode": "TH68023A",
"shadowListed": false,
"offenceWording": "Before 10 Jul 2025 at df attempted to rob dfd of dfdf to the value of sds .",
"statementOfOffence": {
"title": "Attempt robbery",
"welshTitle": "Ymgais i ysbeilio (robbery)",
"legislation": "Contrary to section 1(1) of the Criminal Attempts Act 1981.",
"welshLegislation": "Yn groes i adran 1(1) Deddf Ymgeisiau i Droseddu 1981."
},
"restrictFromCourtList": false
}
],
"firstName": "Teat1",
"bailStatus": {
"id": "86009c70-759d-3308-8de4-194886ff9a77",
"code": "A",
"description": "Not applicable"
},
"dateOfBirth": "1984-01-12",
"masterDefendantId": "4e7853ab-67f8-4ebe-9a31-dc1ed271ebb9",
"hearingLanguageNeeds": "ENGLISH",
"restrictFromCourtList": false,
"nationalityDescription": "British",
"courtProceedingsInitiated": "2025-07-21T13:19:54.727Z"
}
],
"shadowListed": false,
"caseIdentifier": {
"authorityId": "31af405e-7b60-4dd8-a244-c24c2d3fa595",
"authorityCode": "TFL",
"caseReference": "CIK2JQKECS"
},
"restrictFromCourtList": false
}
],
"courtCentreId": "f8254db1-1683-483e-afb3-b87fde5a0a26",
"isSlotsBooked": false,
"nonDefaultDays": [
{
"roomId": "9e4932f7-97b2-3010-b942-ddd2624e4dd8",
"duration": 20,
"startTime": "2025-07-21T15:00:00.000Z",
"courtCentreId": "f8254db1-1683-483e-afb3-b87fde5a0a26"
}
],
"nonSittingDays": [],
"hearingLanguage": "ENGLISH",
"estimatedMinutes": 20,
"jurisdictionType": "MAGISTRATES",
"courtApplications": [],
"courtCentreDetails": {
"id": "f8254db1-1683-483e-afb3-b87fde5a0a26",
"defaultDuration": 420,
"defaultStartTime": "10:00:00"
},
"isGroupProceedings": false,
"numberOfGroupCases": 1,
"sendNotificationToParties": false
}
]
}
Loading