resultMatcher) {
final RequestParams requestParams = requestParams(getURL("hearing.get.hearings", date, startTime, endTime, courtCentreId, roomId), "application/vnd.hearing.get.hearings+json")
diff --git a/hearing-integration-test/src/test/java/uk/gov/moj/cpp/hearing/steps/CourtListRestrictionSteps.java b/hearing-integration-test/src/test/java/uk/gov/moj/cpp/hearing/steps/CourtListRestrictionSteps.java
index 65423c2edd..cf1ddea89d 100644
--- a/hearing-integration-test/src/test/java/uk/gov/moj/cpp/hearing/steps/CourtListRestrictionSteps.java
+++ b/hearing-integration-test/src/test/java/uk/gov/moj/cpp/hearing/steps/CourtListRestrictionSteps.java
@@ -3,11 +3,22 @@
import static com.google.common.collect.Lists.newArrayList;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.isJson;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.withJsonPath;
+import static java.text.MessageFormat.format;
import static java.util.UUID.fromString;
import static java.util.UUID.randomUUID;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static javax.ws.rs.core.Response.Status.OK;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static uk.gov.justice.services.common.http.HeaderConstants.USER_ID;
+import static uk.gov.justice.services.test.utils.core.http.BaseUriProvider.getBaseUri;
+import static uk.gov.justice.services.test.utils.core.http.RequestParamsBuilder.requestParams;
+import static uk.gov.justice.services.test.utils.core.http.RestPoller.poll;
+import static uk.gov.justice.services.test.utils.core.matchers.ResponsePayloadMatcher.payload;
+import static uk.gov.justice.services.test.utils.core.matchers.ResponseStatusMatcher.status;
+import static uk.gov.moj.cpp.hearing.utils.WireMockStubUtils.setupAsAuthorizedAndSystemUser;
import static uk.gov.justice.hearing.courts.CourtListRestricted.courtListRestricted;
import static uk.gov.justice.services.test.utils.core.messaging.MetadataBuilderFactory.metadataWithRandomUUID;
import static uk.gov.moj.cpp.hearing.it.UseCases.asDefault;
@@ -32,6 +43,10 @@
import uk.gov.moj.cpp.hearing.test.CommandHelpers;
import java.security.NoSuchAlgorithmException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.util.Optional;
@@ -42,8 +57,11 @@
import io.restassured.path.json.JsonPath;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.awaitility.Awaitility;
import org.hamcrest.Matcher;
import org.junit.jupiter.api.BeforeEach;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class CourtListRestrictionSteps extends AbstractIT {
@@ -87,6 +105,103 @@ public JsonPath hearingEventsCourtListRestrictedReceived(final Matcher> matche
}
}
+ /**
+ * Polls the publish-side query {@code hearing.latest-hearings-by-court-centres} until the
+ * restriction projection has reached the expected state. The publish flow internally consumes
+ * the same query — once it reflects the toggle, the next {@code publish-court-list} command is
+ * guaranteed to see the same state.
+ *
+ * Required because {@link #hearingEventsCourtListRestrictedReceived(Matcher)} only confirms the
+ * hearing event was emitted; the listener that projects it into the JPA entity runs in a
+ * separate transaction and may lag behind the publish command if not waited for.
+ *
+ * A hearing-visibility precondition ({@code courtRoomId notNullValue}) is prepended to the
+ * caller's matcher to prevent the poll from short-circuiting on the empty/not-yet-projected
+ * state — without this, lenient matchers such as {@code hasNoJsonPath(...)} or
+ * {@code withJsonPath(..., hasSize(0))} would match an empty {@code {}} response and return
+ * before the restriction event has actually been processed.
+ */
+ public void waitForRestrictionProjection(final String courtCentreId,
+ final LocalDate hearingDate,
+ final Matcher super com.jayway.jsonpath.ReadContext> expectedPayload) {
+ setupAsAuthorizedAndSystemUser(USER_ID_VALUE_AS_ADMIN);
+ final String queryPart = format(ENDPOINT_PROPERTIES.getProperty("hearing.latest-hearings-by-court-centres"), courtCentreId, hearingDate);
+ final String searchCourtListUrl = String.format("%s/%s", getBaseUri(), queryPart);
+
+ poll(requestParams(searchCourtListUrl, "application/vnd.hearing.latest-hearings-by-court-centres+json")
+ .withHeader(USER_ID, getLoggedInSystemUserHeader()))
+ .timeout(60, SECONDS)
+ .pollInterval(1, SECONDS)
+ .until(status().is(OK), payload().isJson(allOf(
+ withJsonPath("$.court.courtSites[0].courtRooms[0].courtRoomId", notNullValue()),
+ expectedPayload)));
+ }
+
+ /**
+ * Polls the view-store DB directly until the just-created hearing has BOTH
+ * {@code ha_hearing} AND {@code ha_hearing_day} rows for the given courtCentreId/date.
+ * MUST be called after {@code createHearingEvent*} and BEFORE any {@code hide*FromXhibit}
+ * call or any {@code sendPublishCourtListCommand}.
+ *
+ *
Two races this closes
+ *
+ * 1. The listener silent-drop race.
+ * Without this wait, the {@code public.listing.court-list-restricted} → ... →
+ * {@code hearing.event.court-list-restricted} chain can reach
+ * {@link uk.gov.moj.cpp.hearing.event.listener.CourtListRestrictionEventListener} before the
+ * hearing-creation projection has committed to {@code ha_hearing}. The listener does
+ * {@code hearingRepository.findOptionalBy(hearingId)} and, if the row is missing, silently
+ * returns — the message is consumed and never replayed, the restriction is lost, the
+ * subsequent publish reads the un-restricted hearing, and the assertion on the redacted XML
+ * fails.
+ *
+ *
2. The pub-display empty-XML race.
+ * Even when {@code ha_hearing} is populated, the publish's pub-display query
+ * ({@code findHearingsByDateAndCourtCentreList}) INNER-JOINs {@code ha_hearing.hearingDays}
+ * filtered by date. If {@code ha_hearing_day} hasn't been projected yet when the publish
+ * runs, the pub-display query returns thin/empty data and the publish writes an XML with
+ * empty fields (empty courtname, cppurn, defendant fields, no {@code currentstatus} block).
+ * The web-page publish (which goes via {@code ha_hearing_event}) is unaffected and writes
+ * correct XML, so the test sees a mismatch where the same publish call produces correct
+ * web-page XML but stub pub-display XML.
+ *
+ *
Polling JDBC directly is more robust than polling either REST endpoint because both
+ * publish-side queries are gated on the same two tables; once both are populated, both
+ * publishes will see fresh data.
+ */
+ public void waitForHearingVisible(final String courtCentreId, final LocalDate hearingDate) {
+ Awaitility.await()
+ .atMost(60, SECONDS)
+ .pollInterval(500, java.util.concurrent.TimeUnit.MILLISECONDS)
+ .until(() -> hearingProjectedFor(courtCentreId, hearingDate));
+ }
+
+ private boolean hearingProjectedFor(final String courtCentreId, final LocalDate hearingDate) {
+ // Both publish paths need all three tables. Web-page goes via ha_hearing_event;
+ // pub-display additionally INNER-JOINs ha_hearing_day.
+ final String sql = String.format(
+ "SELECT count(1) FROM ha_hearing h " +
+ "INNER JOIN ha_hearing_day day ON day.hearing_id = h.id " +
+ "INNER JOIN ha_hearing_event ev ON ev.hearing_id = h.id " +
+ "WHERE h.court_centre_id = '%s' " +
+ "AND day.date = '%s' " +
+ "AND ev.event_date = '%s' " +
+ "AND ev.deleted = false",
+ courtCentreId, hearingDate, hearingDate);
+ try (final Connection connection = testJdbcConnectionProvider.getViewStoreConnection("hearing");
+ final Statement statement = connection.createStatement();
+ final ResultSet resultSet = statement.executeQuery(sql)) {
+ if (resultSet.next()) {
+ return resultSet.getInt(1) > 0;
+ }
+ } catch (final SQLException e) {
+ HEARING_VISIBILITY_LOGGER.warn("Failed to query view store for visibility check: {}", e.getMessage());
+ }
+ return false;
+ }
+
+ private static final Logger HEARING_VISIBILITY_LOGGER = LoggerFactory.getLogger(CourtListRestrictionSteps.class);
+
private void sendListingPublicEvent(final JsonObject restrictCourtListDataObject) {
sendMessage(
getPublicTopicInstance().createProducer(),
@@ -99,11 +214,12 @@ public CommandHelpers.InitiateHearingCommandHelper createHearingEvent(final UUID
final UUID eventDefinitionId, final ZonedDateTime eventTime, final Optional hearingTypeId, String courtCenter, LocalDate localDate) throws NoSuchAlgorithmException {
final CommandHelpers.InitiateHearingCommandHelper hearing = h(UseCases.initiateHearingWithNsp(getRequestSpec(), initiateHearingTemplateWithParamNoReportingRestriction(fromString(courtCenter), fromString(courtRoomId), "CourtRoom 1", localDate, fromString(defenceCounselId), caseId, hearingTypeId)));
logEvent(hearingEventId, getRequestSpec(), asDefault(), hearing.it(), eventDefinitionId, false, fromString(defenceCounselId), eventTime, null);
+ waitForHearingVisible(courtCenter, eventTime.toLocalDate());
return hearing;
}
public CommandHelpers.InitiateHearingCommandHelper createHearingEventWithYoungDefendant(final UUID caseId, final UUID hearingEventId, final String courtRoomId, final String defenceCounselId,
- final UUID eventDefinitionId, final ZonedDateTime eventTime, final Optional hearingTypeId, final String courtCenter, final LocalDate localDate) throws NoSuchAlgorithmException {
+ final UUID eventDefinitionId, final ZonedDateTime eventTime, final Optional hearingTypeId, final String courtCenter, final LocalDate localDate) throws NoSuchAlgorithmException {
try (final Utilities.EventListener eventListener = listenFor(HEARING_EVENTS_COURT_LIST_RESTRICTED, HEARING_EVENT)
.withFilter(isJson(allOf(
withJsonPath("$.defendantIds", hasSize(1)),
@@ -112,6 +228,7 @@ public CommandHelpers.InitiateHearingCommandHelper createHearingEventWithYoungDe
initiateHearingTemplateWithParamNoReportingRestrictionYoungDefendant(fromString(courtCenter), fromString(courtRoomId), "CourtRoom 1", localDate, fromString(defenceCounselId), caseId, hearingTypeId)));
logEvent(hearingEventId, getRequestSpec(), asDefault(), hearing.it(), eventDefinitionId, false, fromString(defenceCounselId), eventTime, null);
eventListener.waitFor();
+ waitForHearingVisible(courtCenter, eventTime.toLocalDate());
return hearing;
}
}
@@ -121,6 +238,7 @@ public CommandHelpers.InitiateHearingCommandHelper createHearingEventForApplicat
final CommandHelpers.InitiateHearingCommandHelper hearing = h(initiateHearingForApplication(getRequestSpec(), initiateHearingTemplateForApplicationNoReportingRestriction(fromString(courtCenter), fromString(courtRoomId), "CourtRoom 1", localDate, fromString(defenceCounselId), caseId, hearingTypeId)));
givenAUserHasLoggedInAsACourtClerk(randomUUID());
logEvent(hearingEventId, getRequestSpec(), asDefault(), hearing.it(), eventDefinitionId, false, fromString(defenceCounselId), eventTime, null);
+ waitForHearingVisible(courtCenter, eventTime.toLocalDate());
return hearing;
}
diff --git a/hearing-integration-test/src/test/java/uk/gov/moj/cpp/hearing/utils/WebDavStub.java b/hearing-integration-test/src/test/java/uk/gov/moj/cpp/hearing/utils/WebDavStub.java
index 7c30374ed5..8411194ead 100644
--- a/hearing-integration-test/src/test/java/uk/gov/moj/cpp/hearing/utils/WebDavStub.java
+++ b/hearing-integration-test/src/test/java/uk/gov/moj/cpp/hearing/utils/WebDavStub.java
@@ -7,11 +7,13 @@
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching;
import static javax.ws.rs.core.Response.Status.OK;
+import static org.awaitility.Durations.ONE_MINUTE;
import java.util.List;
import java.util.UUID;
import com.github.tomakehurst.wiremock.verification.LoggedRequest;
+import org.awaitility.Awaitility;
public class WebDavStub {
@@ -35,4 +37,22 @@ public static String getFileForPath(final String filePath) {
return loggedRequest.getBodyAsString();
}
+
+ public static int countFilesAt(final String filePath) {
+ return findAll(putRequestedFor(urlPathMatching(filePath))).size();
+ }
+
+ public static int countSentXmlForPubDisplay() {
+ return countFilesAt(XHIBIT_GATEWAY_SEND_PUB_DISP_TO_XHIBIT_FILE_PATH_REG_EX);
+ }
+
+ public static void awaitNewFile(final String filePath, final int previousCount) {
+ Awaitility.await()
+ .atMost(ONE_MINUTE)
+ .until(() -> countFilesAt(filePath) > previousCount);
+ }
+
+ public static void awaitNewSentXmlForPubDisplay(final int previousCount) {
+ awaitNewFile(XHIBIT_GATEWAY_SEND_PUB_DISP_TO_XHIBIT_FILE_PATH_REG_EX, previousCount);
+ }
}
diff --git a/hearing-integration-test/src/test/resources/endpoint.properties b/hearing-integration-test/src/test/resources/endpoint.properties
index eb624b352c..410331f61f 100644
--- a/hearing-integration-test/src/test/resources/endpoint.properties
+++ b/hearing-integration-test/src/test/resources/endpoint.properties
@@ -3,6 +3,7 @@ hearing.initiate=hearing-service/command/api/rest/hearing/hearings
hearing.update-related-hearing=hearing-service/command/api/rest/hearing/hearings/{0}
hearing.update-hearing=hearing-service/command/api/rest/hearing/hearings/{0}
hearing.get.hearing=/hearing-service/query/api/rest/hearing/hearings/{0}
+hearing.get.hearing-for-manage-hearing=/hearing-service/query/api/rest/hearing/hearings/{0}
hearing.get.hearings=/hearing-service/query/api/rest/hearing/hearings?date={0}&startTime={1}&endTime={2}&courtCentreId={3}&roomId={4}
hearing.get.hearings-for-today=/hearing-service/query/api/rest/hearing/hearings-for-today
hearing.get-draft-result=hearing-service/query/api/rest/hearing/hearings/{0}/draft-result
diff --git a/hearing-json/pom.xml b/hearing-json/pom.xml
index 1feabf0587..a7b2b9b3b8 100644
--- a/hearing-json/pom.xml
+++ b/hearing-json/pom.xml
@@ -3,7 +3,7 @@
hearing-parent
uk.gov.moj.cpp.hearing
- 17.104.158-SNAPSHOT
+ 17.104.158-CCSPH2-SNAPSHOT
4.0.0
diff --git a/hearing-query/hearing-query-api/pom.xml b/hearing-query/hearing-query-api/pom.xml
index 5dcaac0dc2..83a856feed 100644
--- a/hearing-query/hearing-query-api/pom.xml
+++ b/hearing-query/hearing-query-api/pom.xml
@@ -3,7 +3,7 @@
uk.gov.moj.cpp.hearing
hearing-query
- 17.104.158-SNAPSHOT
+ 17.104.158-CCSPH2-SNAPSHOT
hearing-query-api
war
diff --git a/hearing-query/hearing-query-api/src/main/java/uk/gov/moj/cpp/hearing/query/api/HearingQueryApi.java b/hearing-query/hearing-query-api/src/main/java/uk/gov/moj/cpp/hearing/query/api/HearingQueryApi.java
index a4d5db4690..bb5ee59152 100644
--- a/hearing-query/hearing-query-api/src/main/java/uk/gov/moj/cpp/hearing/query/api/HearingQueryApi.java
+++ b/hearing-query/hearing-query-api/src/main/java/uk/gov/moj/cpp/hearing/query/api/HearingQueryApi.java
@@ -12,6 +12,7 @@
import uk.gov.justice.core.courts.CrackedIneffectiveTrial;
import uk.gov.justice.hearing.courts.GetHearings;
import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter;
+import uk.gov.justice.services.common.converter.ObjectToJsonObjectConverter;
import uk.gov.justice.services.core.annotation.Component;
import uk.gov.justice.services.core.annotation.Handles;
import uk.gov.justice.services.core.annotation.ServiceComponent;
@@ -136,6 +137,8 @@ public class HearingQueryApi {
@Inject
private HearingService hearingService;
+ @Inject
+ private ObjectToJsonObjectConverter objectToJsonObjectConverter;
@Handles("hearing.get.hearings")
public JsonEnvelope findHearings(final JsonEnvelope query) {
@@ -184,6 +187,16 @@ public JsonEnvelope findHearing(final JsonEnvelope query) {
return getJsonEnvelope(envelope);
}
+ @Handles("hearing.get.hearing-for-manage-hearing")
+ public JsonEnvelope findHearingForManageHearing(final JsonEnvelope query) {
+ final JsonEnvelope jsonEnvelope = findHearing(query);
+
+ final HearingDetailsResponse hearingDetailsResponse = jsonObjectToObjectConverter.convert(jsonEnvelope.payloadAsJsonObject(), HearingDetailsResponse.class);
+
+ return envelopeFrom(metadataFrom(jsonEnvelope.metadata()),objectToJsonObjectConverter.convert(hearingService.filterOutProsecutionCases(hearingDetailsResponse)));
+ }
+
+
@Handles("hearing.get-hearing-event-definitions")
public JsonEnvelope getHearingEventDefinitionsVersionTwo(final JsonEnvelope query) {
final Envelope envelope = this.hearingEventQueryView.getHearingEventDefinitions(query);
diff --git a/hearing-query/hearing-query-api/src/main/resources/uk/gov/moj/cpp/hearing/query/api/accesscontrol/hearing-query-api.drl b/hearing-query/hearing-query-api/src/main/resources/uk/gov/moj/cpp/hearing/query/api/accesscontrol/hearing-query-api.drl
index 7c6888da25..a16f6df75f 100644
--- a/hearing-query/hearing-query-api/src/main/resources/uk/gov/moj/cpp/hearing/query/api/accesscontrol/hearing-query-api.drl
+++ b/hearing-query/hearing-query-api/src/main/resources/uk/gov/moj/cpp/hearing/query/api/accesscontrol/hearing-query-api.drl
@@ -42,6 +42,15 @@ rule "Query - API - hearing.get.hearing"
$outcome.setSuccess(true);
end
+rule "Query - API - hearing.get.hearing-for-manage-hearing"
+ when
+ $outcome: Outcome();
+ $action: Action(name == "hearing.get.hearing-for-manage-hearing");
+ eval(userAndGroupProvider.isMemberOfAnyOfTheSuppliedGroups($action, "Listing Officers", "Court Clerks", "Legal Advisers", "System Users", "Judiciary", "Court Associate", "Deputies", "DJMC", "Judge", "Recorders", "Court Administrators"));
+ then
+ $outcome.setSuccess(true);
+end
+
rule "Query - API - hearing.get-hearing-event-definitions"
when
$outcome: Outcome();
diff --git a/hearing-query/hearing-query-api/src/raml/hearing-query-api.raml b/hearing-query/hearing-query-api/src/raml/hearing-query-api.raml
index 7f0bcd40fa..5088a6237a 100644
--- a/hearing-query/hearing-query-api/src/raml/hearing-query-api.raml
+++ b/hearing-query/hearing-query-api/src/raml/hearing-query-api.raml
@@ -191,6 +191,9 @@ protocols: [ HTTP, HTTPS ]
(mapping):
responseType: application/vnd.hearing.get.hearing+json
name: hearing.get.hearing
+ (mapping):
+ responseType: application/vnd.hearing.get.hearing-for-manage-hearing+json
+ name: hearing.get.hearing-for-manage-hearing
...
responses:
200:
@@ -199,6 +202,9 @@ protocols: [ HTTP, HTTPS ]
application/vnd.hearing.get.hearing+json:
example: !include json/hearing.get.hearing.json
schema: !include json/schema/hearing.get.hearing.json
+ application/vnd.hearing.get.hearing-for-manage-hearing+json:
+ example: !include json/hearing.get.hearing.json
+ schema: !include json/schema/hearing.get.hearing.json
/hearings:
get:
diff --git a/hearing-query/hearing-query-api/src/test/java/uk/gov/moj/cpp/hearing/query/api/FindHearingQueryApiTest.java b/hearing-query/hearing-query-api/src/test/java/uk/gov/moj/cpp/hearing/query/api/FindHearingQueryApiTest.java
index ef3ef09e8a..1a6e781a98 100644
--- a/hearing-query/hearing-query-api/src/test/java/uk/gov/moj/cpp/hearing/query/api/FindHearingQueryApiTest.java
+++ b/hearing-query/hearing-query-api/src/test/java/uk/gov/moj/cpp/hearing/query/api/FindHearingQueryApiTest.java
@@ -7,6 +7,7 @@
import static org.mockito.Mockito.when;
import static uk.gov.justice.services.test.utils.core.enveloper.EnveloperFactory.createEnveloper;
+import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter;
import uk.gov.justice.services.core.dispatcher.EnvelopePayloadTypeConverter;
import uk.gov.justice.services.core.dispatcher.JsonEnvelopeRepacker;
import uk.gov.justice.services.core.enveloper.Enveloper;
@@ -85,6 +86,9 @@ public class FindHearingQueryApiTest {
@Mock
private EnvelopePayloadTypeConverter envelopePayloadTypeConverter;
+ @Mock
+ private JsonObjectToObjectConverter jsonObjectToObjectConverter;
+
@Spy
private Enveloper enveloper = createEnveloper();
@@ -166,8 +170,16 @@ public void should_return_hearing_for_recorder() {
}
+ @Test
+ public void should_throw_bad_request_when_user_id_is_missing_ForManageHearing() {
+ when(jsonInputEnvelope.metadata()).thenReturn(metadata);
+ when(metadata.userId()).thenReturn(Optional.empty());
+
+ assertThrows(BadRequestException.class, () -> hearingQueryApi.findHearingForManageHearing(jsonInputEnvelope));
+ }
+
private CrackedIneffectiveVacatedTrialTypes getCrackedIneffectiveVacatedTrialTypes() {
- final CrackedIneffectiveVacatedTrialType crackedIneffectiveVacatedTrialType = new CrackedIneffectiveVacatedTrialType(randomUUID(), "", "", "","", LocalDate.now());
+ final CrackedIneffectiveVacatedTrialType crackedIneffectiveVacatedTrialType = new CrackedIneffectiveVacatedTrialType(randomUUID(), "", "", "", "", LocalDate.now());
final List crackedIneffectiveVacatedTrialTypes = new ArrayList();
crackedIneffectiveVacatedTrialTypes.add(crackedIneffectiveVacatedTrialType);
diff --git a/hearing-query/hearing-query-api/src/test/java/uk/gov/moj/cpp/hearing/query/api/HearingQueryApiTest.java b/hearing-query/hearing-query-api/src/test/java/uk/gov/moj/cpp/hearing/query/api/HearingQueryApiTest.java
index 35f63d2620..699faebfca 100644
--- a/hearing-query/hearing-query-api/src/test/java/uk/gov/moj/cpp/hearing/query/api/HearingQueryApiTest.java
+++ b/hearing-query/hearing-query-api/src/test/java/uk/gov/moj/cpp/hearing/query/api/HearingQueryApiTest.java
@@ -9,8 +9,12 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -18,6 +22,8 @@
import uk.gov.justice.core.courts.CrackedIneffectiveTrial;
import uk.gov.justice.hearing.courts.GetHearings;
+import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter;
+import uk.gov.justice.services.common.converter.ObjectToJsonObjectConverter;
import uk.gov.justice.services.core.annotation.Handles;
import uk.gov.justice.services.core.dispatcher.EnvelopePayloadTypeConverter;
import uk.gov.justice.services.core.dispatcher.JsonEnvelopeRepacker;
@@ -29,6 +35,12 @@
import uk.gov.moj.cpp.external.domain.progression.prosecutioncases.ProsecutionCase;
import uk.gov.moj.cpp.hearing.event.nowsdomain.referencedata.nows.CrackedIneffectiveVacatedTrialTypes;
import uk.gov.moj.cpp.hearing.event.nowsdomain.referencedata.resultdefinition.Prompt;
+import uk.gov.moj.cpp.hearing.query.api.service.accessfilter.AccessibleApplications;
+import uk.gov.moj.cpp.hearing.query.api.service.accessfilter.AccessibleCases;
+import uk.gov.moj.cpp.hearing.query.api.service.accessfilter.DDJChecker;
+import uk.gov.moj.cpp.hearing.query.api.service.accessfilter.RecorderChecker;
+import uk.gov.moj.cpp.hearing.query.api.service.accessfilter.UsersAndGroupsService;
+import uk.gov.moj.cpp.hearing.query.api.service.accessfilter.vo.Permissions;
import uk.gov.moj.cpp.hearing.query.api.service.progression.ProgressionService;
import uk.gov.moj.cpp.hearing.query.api.service.referencedata.PIEventMapperCache;
import uk.gov.moj.cpp.hearing.query.api.service.referencedata.ReferenceDataService;
@@ -41,9 +53,11 @@
import uk.gov.moj.cpp.hearing.query.view.response.Timeline;
import uk.gov.moj.cpp.hearing.query.view.response.TimelineHearingSummary;
import uk.gov.moj.cpp.hearing.query.view.response.hearingresponse.GetShareResultsV2Response;
+import uk.gov.moj.cpp.hearing.query.view.response.hearingresponse.HearingDetailsResponse;
import uk.gov.moj.cpp.hearing.query.view.response.hearingresponse.NowListResponse;
import uk.gov.moj.cpp.hearing.query.view.response.hearingresponse.ProsecutionCaseResponse;
import uk.gov.moj.cpp.hearing.query.view.response.hearingresponse.TargetListResponse;
+import uk.gov.moj.cpp.hearing.query.view.service.HearingService;
import java.io.File;
import java.lang.reflect.Method;
@@ -52,6 +66,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -165,6 +180,36 @@ public class HearingQueryApiTest {
@Mock
private UserGroupQueryService userGroupQueryService;
+ @Mock
+ private UsersAndGroupsService usersAndGroupsService;
+
+ @Mock
+ private DDJChecker ddjChecker;
+
+ @Mock
+ private RecorderChecker recorderChecker;
+
+ @Mock
+ private AccessibleCases accessibleCases;
+
+ @Mock
+ private AccessibleApplications accessibleApplications;
+
+ @Mock
+ private HearingService hearingService;
+
+ @Mock
+ private JsonObjectToObjectConverter jsonObjectToObjectConverter;
+
+ @Mock
+ private ObjectToJsonObjectConverter objectToJsonObjectConverter;
+
+ @Mock
+ private Permissions mockPermissions;
+
+ @Mock
+ private Envelope mockHearingDetailsResponseEnvelope;
+
private Map apiMethodsToHandlerNames;
@BeforeEach
@@ -440,6 +485,41 @@ public void shouldNotProcessGetHearingEventLogCountForNonHMCTSUser() {
verify(hearingEventQueryView, times(0)).getHearingEventLogCount(any(JsonEnvelope.class));
}
+ @Test
+ public void shouldFindHearingForManageHearing() {
+ final UUID userId = randomUUID();
+ final UUID hearingId = randomUUID();
+
+ final JsonEnvelope query = mock(JsonEnvelope.class, RETURNS_DEEP_STUBS);
+ when(query.metadata().userId()).thenReturn(Optional.of(userId.toString()));
+ when(query.payloadAsJsonObject()).thenReturn(createObjectBuilder().add("hearingId", hearingId.toString()).build());
+
+ when(referenceDataService.listAllCrackedIneffectiveVacatedTrialTypes()).thenReturn(crackedIneffectiveVacatedTrialTypes);
+ when(usersAndGroupsService.permissions(userId.toString())).thenReturn(mockPermissions);
+ when(ddjChecker.isDDJ(mockPermissions)).thenReturn(false);
+ when(recorderChecker.isRecorder(mockPermissions)).thenReturn(false);
+ when(hearingQueryView.findHearing(any(), any(), any(), anyBoolean())).thenReturn(mockHearingDetailsResponseEnvelope);
+ when(mockEnvelopePayloadTypeConverter.convert(any(), any(Class.class))).thenReturn(mockJsonValueEnvelope);
+
+ final JsonEnvelope repackedEnvelope = EnvelopeFactory.createEnvelope("hearing.get.hearing", createObjectBuilder().add("hearingId", hearingId.toString()).build());
+ when(mockJsonEnvelopeRepacker.repack(mockJsonValueEnvelope)).thenReturn(repackedEnvelope);
+
+ final HearingDetailsResponse hearingDetailsResponse = mock(HearingDetailsResponse.class);
+ final HearingDetailsResponse filteredResponse = mock(HearingDetailsResponse.class);
+ final javax.json.JsonObject filteredJsonObject = createObjectBuilder().add("hearingId", hearingId.toString()).build();
+
+ when(jsonObjectToObjectConverter.convert(any(javax.json.JsonObject.class), eq(HearingDetailsResponse.class))).thenReturn(hearingDetailsResponse);
+ when(hearingService.filterOutProsecutionCases(hearingDetailsResponse)).thenReturn(filteredResponse);
+ when(objectToJsonObjectConverter.convert(filteredResponse)).thenReturn(filteredJsonObject);
+
+ final JsonEnvelope result = hearingQueryApi.findHearingForManageHearing(query);
+
+ verify(hearingService).validateUserPermissionForApplicationType(query);
+ verify(hearingService).filterOutProsecutionCases(hearingDetailsResponse);
+ verify(objectToJsonObjectConverter).convert(filteredResponse);
+ assertThat(result, is(notNullValue()));
+ }
+
private Set buildPIEventCache() {
final UUID cpHearingEventId_1 = randomUUID();
final UUID cpHearingEventId_2 = UUID.fromString("abdaeb88-8952-4c07-99c4-d27c39d4e63a");
diff --git a/hearing-query/hearing-query-view/pom.xml b/hearing-query/hearing-query-view/pom.xml
index 0251488a09..08f486c18b 100644
--- a/hearing-query/hearing-query-view/pom.xml
+++ b/hearing-query/hearing-query-view/pom.xml
@@ -3,7 +3,7 @@
uk.gov.moj.cpp.hearing
hearing-query
- 17.104.158-SNAPSHOT
+ 17.104.158-CCSPH2-SNAPSHOT
hearing-query-view
jar
diff --git a/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/HearingEventQueryView.java b/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/HearingEventQueryView.java
index 01001161e0..67db383ab8 100644
--- a/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/HearingEventQueryView.java
+++ b/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/HearingEventQueryView.java
@@ -26,6 +26,7 @@
import uk.gov.moj.cpp.hearing.mapping.CourtApplicationsSerializer;
import uk.gov.moj.cpp.hearing.persist.entity.ha.CourtCentre;
import uk.gov.moj.cpp.hearing.persist.entity.ha.Hearing;
+import uk.gov.moj.cpp.hearing.persist.entity.ha.HearingDay;
import uk.gov.moj.cpp.hearing.persist.entity.ha.HearingDefenceCounsel;
import uk.gov.moj.cpp.hearing.persist.entity.ha.HearingEvent;
import uk.gov.moj.cpp.hearing.persist.entity.ha.HearingProsecutionCounsel;
@@ -555,13 +556,14 @@ private List getActiveHearingsForCourtRoom(final UUID hearingId, final Loc
return Collections.emptyList();
}
- final CourtCentre courtCentre = optionalCourtCentre.get();
+ final CourtCentre topLevel = optionalCourtCentre.get();
+ final Optional matchedDay = hearingService.getHearingDayByHearingIdAndDate(hearingId, date);
+
+ final UUID centreId = matchedDay.map(HearingDay::getCourtCentreId).filter(java.util.Objects::nonNull).orElse(topLevel.getId());
+ final UUID roomId = matchedDay.map(HearingDay::getCourtRoomId).filter(java.util.Objects::nonNull).orElse(topLevel.getRoomId());
+
final List hearingEvents =
- hearingService
- .getHearingEvents(
- courtCentre.getId(),
- courtCentre.getRoomId(),
- date);
+ hearingService.getHearingEvents(centreId, roomId, date);
return getActiveHearingIdsByHearingEvents(hearingEvents);
}
diff --git a/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/service/HearingListXhibitResponseTransformer.java b/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/service/HearingListXhibitResponseTransformer.java
index 11a1aab736..20129223ee 100644
--- a/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/service/HearingListXhibitResponseTransformer.java
+++ b/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/service/HearingListXhibitResponseTransformer.java
@@ -41,6 +41,7 @@
import uk.gov.moj.cpp.listing.domain.referencedata.CourtRoomMapping;
import java.math.BigInteger;
+import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
@@ -74,30 +75,35 @@ public class HearingListXhibitResponseTransformer {
private static final DateTimeFormatter dateTimeFormatter = ofPattern("yyyy-MM-dd'T'HH:mm'Z'");
public CurrentCourtStatus transformFrom(final HearingEventsToHearingMapper hearingEventsToHearingMapper) {
+ return transformFrom(hearingEventsToHearingMapper, null);
+ }
+
+ public CurrentCourtStatus transformFrom(final HearingEventsToHearingMapper hearingEventsToHearingMapper, final LocalDate publishDate) {
return currentCourtStatus()
- .withCourt(getCourt(hearingEventsToHearingMapper))
+ .withCourt(getCourt(hearingEventsToHearingMapper, publishDate))
.build();
}
- private Court getCourt(final HearingEventsToHearingMapper hearingEventsToHearingMapper) {
+ private Court getCourt(final HearingEventsToHearingMapper hearingEventsToHearingMapper, final LocalDate publishDate) {
final Map courtSiteMap = new HashMap<>();
return court()
//Logically all hearings will belong to single court centre, therefore we pick up the first one
.withCourtName(hearingEventsToHearingMapper.getHearingList().get(0).getCourtCentre().getName())
- .withCourtSites(getCourtSites(hearingEventsToHearingMapper, courtSiteMap))
+ .withCourtSites(getCourtSites(hearingEventsToHearingMapper, courtSiteMap, publishDate))
.build();
}
- private List getCourtSites(final HearingEventsToHearingMapper hearingEventsToHearingMapper, final Map courtSiteMap) {
+ private List getCourtSites(final HearingEventsToHearingMapper hearingEventsToHearingMapper, final Map courtSiteMap, final LocalDate publishDate) {
return hearingEventsToHearingMapper.getHearingList()
.stream()
- .map(hearing -> getCourtSite(hearingEventsToHearingMapper, hearing, courtSiteMap))
+ .map(hearing -> getCourtSite(hearingEventsToHearingMapper, hearing, courtSiteMap, publishDate))
.distinct()
.collect(toList());
}
- private CourtSite getCourtSite(final HearingEventsToHearingMapper hearingEventsToHearingMapper, final Hearing hearing, final Map courtSiteMap) {
- final CourtRoomMapping courtRoomMapping = commonXhibitReferenceDataService.getCourtRoomMappingBy(hearing.getCourtCentre().getId(), hearing.getCourtCentre().getRoomId());
+ private CourtSite getCourtSite(final HearingEventsToHearingMapper hearingEventsToHearingMapper, final Hearing hearing, final Map courtSiteMap, final LocalDate publishDate) {
+ final ResolvedRoom resolved = resolveCentreAndRoom(hearing, publishDate);
+ final CourtRoomMapping courtRoomMapping = commonXhibitReferenceDataService.getCourtRoomMappingBy(resolved.centreId, resolved.roomId);
CourtSite courtSite = courtSiteMap.get(courtRoomMapping.getCrestCourtSiteUUID());
if (courtSite == null) {
courtSite = courtSite()
@@ -106,31 +112,34 @@ private CourtSite getCourtSite(final HearingEventsToHearingMapper hearingEventsT
.withCourtRooms(new ArrayList<>())
.build();
}
- courtSite.getCourtRooms().addAll(getCourtRoomsForCourtSite(hearingEventsToHearingMapper, courtRoomMapping.getCrestCourtSiteUUID()));
+ courtSite.getCourtRooms().addAll(getCourtRoomsForCourtSite(hearingEventsToHearingMapper, courtRoomMapping.getCrestCourtSiteUUID(), publishDate));
return courtSite;
}
- private List getCourtRoomsForCourtSite(final HearingEventsToHearingMapper hearingEventsToHearingMapper, final UUID crestCourtSiteId) {
+ private List getCourtRoomsForCourtSite(final HearingEventsToHearingMapper hearingEventsToHearingMapper, final UUID crestCourtSiteId, final LocalDate publishDate) {
final Map courtRoomMap = new HashMap<>();
return hearingEventsToHearingMapper.getHearingList()
.stream()
- .filter(hearing -> isHearingForCourtSite(crestCourtSiteId, hearing))
- .map(hearing -> getCourtRoom(hearingEventsToHearingMapper, hearing, courtRoomMap))
+ .filter(hearing -> isHearingForCourtSite(crestCourtSiteId, hearing, publishDate))
+ .map(hearing -> getCourtRoom(hearingEventsToHearingMapper, hearing, courtRoomMap, publishDate))
.distinct()
.collect(toList());
}
- private boolean isHearingForCourtSite(final UUID crestCourtSiteId, final Hearing hearing) {
- final CourtRoomMapping mapping = commonXhibitReferenceDataService.getCourtRoomMappingBy(hearing.getCourtCentre().getId(), hearing.getCourtCentre().getRoomId());
+ private boolean isHearingForCourtSite(final UUID crestCourtSiteId, final Hearing hearing, final LocalDate publishDate) {
+ final ResolvedRoom resolved = resolveCentreAndRoom(hearing, publishDate);
+ final CourtRoomMapping mapping = commonXhibitReferenceDataService.getCourtRoomMappingBy(resolved.centreId, resolved.roomId);
final UUID siteId = mapping.getCrestCourtSiteUUID();
return crestCourtSiteId != null && crestCourtSiteId.equals(siteId);
}
private CourtRoom getCourtRoom(final HearingEventsToHearingMapper hearingEventsToHearingMapper,
final Hearing hearing,
- final Map courtRoomMap) {
- final UUID courtRoomKey = hearing.getCourtCentre().getRoomId();
- final CourtRoomMapping courtRoomMapping = commonXhibitReferenceDataService.getCourtRoomMappingBy(hearing.getCourtCentre().getId(), hearing.getCourtCentre().getRoomId());
+ final Map courtRoomMap,
+ final LocalDate publishDate) {
+ final ResolvedRoom resolved = resolveCentreAndRoom(hearing, publishDate);
+ final UUID courtRoomKey = resolved.roomId;
+ final CourtRoomMapping courtRoomMapping = commonXhibitReferenceDataService.getCourtRoomMappingBy(resolved.centreId, resolved.roomId);
CourtRoom courtRoom = courtRoomMap.get(courtRoomKey);
final Set activeHearingIds = hearingEventsToHearingMapper.getActiveHearingIds();
@@ -364,4 +373,30 @@ private Set getReportingRestrictionLabel(final Offence offence, final Se
ofNullable(reportingRestriction).ifPresent(restriction -> publicNoticesValue.add(restriction.getLabel())));
return publicNoticesValue;
}
+
+ private ResolvedRoom resolveCentreAndRoom(final Hearing hearing, final LocalDate publishDate) {
+ final UUID topCentreId = hearing.getCourtCentre().getId();
+ final UUID topRoomId = hearing.getCourtCentre().getRoomId();
+ if (publishDate == null || hearing.getHearingDays() == null) {
+ return new ResolvedRoom(topCentreId, topRoomId);
+ }
+ return hearing.getHearingDays().stream()
+ .filter(day -> nonNull(day.getSittingDay())
+ && day.getSittingDay().toLocalDate().equals(publishDate))
+ .findFirst()
+ .map(day -> new ResolvedRoom(
+ nonNull(day.getCourtCentreId()) ? day.getCourtCentreId() : topCentreId,
+ nonNull(day.getCourtRoomId()) ? day.getCourtRoomId() : topRoomId))
+ .orElse(new ResolvedRoom(topCentreId, topRoomId));
+ }
+
+ private static final class ResolvedRoom {
+ private final UUID centreId;
+ private final UUID roomId;
+
+ ResolvedRoom(final UUID centreId, final UUID roomId) {
+ this.centreId = centreId;
+ this.roomId = roomId;
+ }
+ }
}
diff --git a/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/service/HearingService.java b/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/service/HearingService.java
index dcc68fc1fc..465a73674f 100644
--- a/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/service/HearingService.java
+++ b/hearing-query/hearing-query-view/src/main/java/uk/gov/moj/cpp/hearing/query/view/service/HearingService.java
@@ -13,6 +13,7 @@
import static java.util.Optional.ofNullable;
import static java.util.UUID.fromString;
import static java.util.stream.Collectors.toList;
+import static org.apache.commons.collections.CollectionUtils.isEmpty;
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
import static uk.gov.justice.core.courts.ApplicationStatus.EJECTED;
import static uk.gov.justice.core.courts.JurisdictionType.CROWN;
@@ -30,7 +31,6 @@
import uk.gov.justice.hearing.courts.HearingSummaries;
import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter;
import uk.gov.justice.services.common.converter.ObjectToJsonObjectConverter;
-import uk.gov.justice.services.common.converter.StringToJsonObjectConverter;
import uk.gov.justice.services.common.exception.ForbiddenRequestException;
import uk.gov.justice.services.common.util.UtcClock;
import uk.gov.justice.services.core.annotation.ServiceComponent;
@@ -48,7 +48,6 @@
import uk.gov.moj.cpp.hearing.event.nowsdomain.referencedata.nows.CrackedIneffectiveVacatedTrialType;
import uk.gov.moj.cpp.hearing.event.nowsdomain.referencedata.nows.CrackedIneffectiveVacatedTrialTypes;
import uk.gov.moj.cpp.hearing.mapping.CourtApplicationsSerializer;
-import uk.gov.moj.cpp.hearing.mapping.DraftResultJPAMapper;
import uk.gov.moj.cpp.hearing.mapping.HearingJPAMapper;
import uk.gov.moj.cpp.hearing.mapping.ProsecutionCaseJPAMapper;
import uk.gov.moj.cpp.hearing.mapping.ResultLineJPAMapper;
@@ -133,7 +132,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-@SuppressWarnings("squid:S1612")
+@SuppressWarnings({"squid:S1612", "squid:S1168"})
public class HearingService {
private static final Logger LOGGER = LoggerFactory.getLogger(HearingService.class);
@@ -141,9 +140,6 @@ public class HearingService {
private static final ZoneId ZONE_ID = ZoneId.of(ZoneOffset.UTC.getId());
private static final UtcClock UTC_CLOCK = new UtcClock();
- static final String COURT_APPLICATIONS = "courtApplications";
- static final String ID = "id";
- static final String APPLICATION_STATUS = "applicationStatus";
@Inject
private HearingRepository hearingRepository;
@@ -162,8 +158,6 @@ public class HearingService {
@Inject
private ObjectToJsonObjectConverter objectToJsonObjectConverter;
@Inject
- private StringToJsonObjectConverter stringToJsonObjectConverter;
- @Inject
private HearingJPAMapper hearingJPAMapper;
@Inject
private ProsecutionCaseJPAMapper prosecutionCaseJPAMapper;
@@ -172,8 +166,6 @@ public class HearingService {
@Inject
private ResultLineJPAMapper resultLineJPAMapper;
@Inject
- private DraftResultJPAMapper draftResultJPAMapper;
- @Inject
private GetHearingsTransformer getHearingTransformer;
@Inject
private TimelineHearingSummaryHelper timelineHearingSummaryHelper;
@@ -233,7 +225,7 @@ public Optional getHearingsForWebPage(final List court
if (!hearingList.isEmpty()) {
final HearingEventsToHearingMapper hearingEventsToHearingMapper = new HearingEventsToHearingMapper(activeHearingEventList, hearingList, activeHearingEventList);
- return Optional.of(hearingListXhibitResponseTransformer.transformFrom(hearingEventsToHearingMapper));
+ return Optional.of(hearingListXhibitResponseTransformer.transformFrom(hearingEventsToHearingMapper, localDate));
}
return empty();
}
@@ -316,7 +308,7 @@ public Optional getHearingsByDate(final List courtCent
final LocalDate localDate,
final Set cppHearingEventIds) {
LOGGER.info("courtCentreList: {}, localDate: {}, cppHearingEventIds: {}", courtCentreList, localDate, cppHearingEventIds);
- if(courtCentreList.isEmpty()){
+ if (courtCentreList.isEmpty()) {
return empty();
}
final List