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
2 changes: 1 addition & 1 deletion listing-command/listing-command-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<artifactId>listing-command</artifactId>
<groupId>uk.gov.moj.cpp.listing</groupId>
<version>17.104.4-SNAPSHOT</version>
<version>17.104.4-MIG-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public class ListingCommandApi {
private static final String LISTING_COMMAND_DELETE_HEARING = "listing.command.delete-hearing";
private static final String LISTING_COMMAND_DELETE_PREVIOUS_HEARINGS_AND_CREATE_NEXT_HEARING = "listing.command.delete-previous-hearings-and-create-next-hearing";
private static final String LISTING_COMMAND_UPDATE_HEARING_DAY_COURT_SCHEDULE = "listing.command.update-hearing-day-court-schedule";
private static final String LISTING_COMMAND_MIGRATE_CROWN_HEARINGS_TO_COURTSCHEDULES = "listing.command.migrate-crown-hearings-to-courtschedules";
public static final String LISTING_COMMAND_UPDATE_HEARING_ADD_CASE_BDF = "listing.command.update-hearing-add-case-bdf";
private static final Logger LOGGER = LoggerFactory.getLogger(ListingCommandApi.class);
private static final String PROSECUTION_CASES = "prosecutionCases";
Expand Down Expand Up @@ -422,6 +423,12 @@ public void handleUpdateHearingDayCourtSchedule(JsonEnvelope envelope) {
envelope.payload()));
}

@Handles("listing.migrate-crown-hearings-to-courtschedules")
public void handleMigrateCrownHearingsToCourtSchedules(JsonEnvelope envelope) {
sender.send(envelopeFrom(metadataFrom(envelope.metadata()).withName(LISTING_COMMAND_MIGRATE_CROWN_HEARINGS_TO_COURTSCHEDULES),
envelope.payload()));
}

@Handles("listing.mark-unallocated-hearing-as-duplicate")
public void handleMarkUnallocatedHearingAsDuplicate(final JsonEnvelope envelope) {
sender.send(envelopeFrom(metadataFrom(envelope.metadata()).withName(LISTING_COMMAND_DUPLICATE_UNALLOCATED_HEARING),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,15 @@ rule "Command - API - Action - listing.update-hearing-day-court-schedule"
$outcome.setSuccess(true);
end

rule "Command - API - Action - listing.migrate-crown-hearings-to-courtschedules"
when
$outcome: Outcome();
$action: Action(name == "listing.migrate-crown-hearings-to-courtschedules");
eval(userAndGroupProvider.isSystemUser($action))
then
$outcome.setSuccess(true);
end

rule "Command - API - Action - listing.mark-unallocated-hearing-as-duplicate"
when
$outcome: Outcome();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"hearings": [
{
"hearingId": "4182b7c6-6393-43f8-97ea-9a7e58b72ba4",
"hearingDayCourtSchedules": [
{
"hearingDate": "2026-06-22",
"courtScheduleId": "df44e775-d809-4836-a14d-5a9f7c2078c1"
}
]
},
{
"hearingId": "7c2b9e10-4d33-4a8e-9b21-0a1f5c6d7e88",
"hearingDayCourtSchedules": [
{
"hearingDate": "2026-07-14",
"courtScheduleId": "1b9a4f02-77c1-4e0a-8d3b-2c5e9f10a644"
},
{
"hearingDate": "2026-07-15",
"courtScheduleId": "2c0b5103-88d2-4f1b-9e4c-3d6fa021b755"
},
{
"hearingDate": "2026-07-16",
"courtScheduleId": "3d1c6214-99e3-401c-af5d-4e70b132c866"
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "http://justice.gov.uk/listing/courts/listing.migrate-crown-hearings-to-courtschedules.json",
"type": "object",
"properties": {
"hearings": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "http://justice.gov.uk/listing/courts/migrate-crown-hearing.json"
}
}
},
"required": [
"hearings"
],
"additionalProperties": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,25 @@ protocols: [ HTTP, HTTPS ]
202:
description: Request accepted

/migrate-crown-hearings-to-courtschedules:
post:
description: |
Bulk-assign courtScheduleId to CROWN hearing days during court-schedule migration.
For each hearing, sets only the courtScheduleId on the matching hearing day; all other attributes are unchanged.
...
(mapping):
requestType: application/vnd.listing.migrate-crown-hearings-to-courtschedules+json
name: listing.migrate-crown-hearings-to-courtschedules
...
body:
application/vnd.listing.migrate-crown-hearings-to-courtschedules+json:
schema: !include json/schema/listing.migrate-crown-hearings-to-courtschedules.json
example: !include json/listing.migrate-crown-hearings-to-courtschedules.json

responses:
202:
description: Request accepted

/delete-hearing/{hearingId}:
post:
description: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class ListingAccessControlTest extends BaseDroolsAccessControlTest {
private static final String ACTION_CREATE_LISTING_NOTE = "listing.command.create-listing-note";
private static final String ACTION_DELETE_LISTING_NOTE = "listing.command.delete-listing-note";
private static final String ACTION_MARK_UNALLOCATED_HEARING_AS_DUPLICATE = "listing.mark-unallocated-hearing-as-duplicate";
private static final String ACTION_MIGRATE_CROWN_HEARINGS_TO_COURTSCHEDULES = "listing.migrate-crown-hearings-to-courtschedules";
private static final String ACTION_DELETE_HEARING = "listing.command.delete-hearing";
private static final String ACTION_DELETE_PREVIOUS_HEARINGS_AND_CREATE_NEXT_HEARING = "listing.delete-previous-hearings-and-create-next-hearing";

Expand Down Expand Up @@ -266,6 +267,26 @@ public void shouldNotAllowNonSystemUserToMarkUnallocatedHearingAsDuplicate() {
assertFailureOutcome(results);
}

@Test
public void shouldAllowSystemUserToMigrateCrownHearingsToCourtSchedules() {
final Action action = createActionFor(ACTION_MIGRATE_CROWN_HEARINGS_TO_COURTSCHEDULES);
given(userAndGroupProvider.isSystemUser(action)).willReturn(true);

final ExecutionResults results = executeRulesWith(action);

assertSuccessfulOutcome(results);
}

@Test
public void shouldNotAllowNonSystemUserToMigrateCrownHearingsToCourtSchedules() {
final Action action = createActionFor(ACTION_MIGRATE_CROWN_HEARINGS_TO_COURTSCHEDULES);
given(userAndGroupProvider.isSystemUser(action)).willReturn(false);

final ExecutionResults results = executeRulesWith(action);

assertFailureOutcome(results);
}

@Test
public void shouldOnlyAllowHMCTSusersToEditNote() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,22 @@ public void shouldDeleteListingNote() {
assertThat(senderJsonEnvelopeCaptor.getValue().metadata().name(), is("listing.command.handler.delete-listing-note"));
}

@Test
public void shouldForwardMigrateCrownHearingsToCourtSchedulesToCommandHandler() {
final JsonEnvelope command = mock(JsonEnvelope.class);
final MetadataBuilder metadataBuilder = metadataWithRandomUUID("listing.migrate-crown-hearings-to-courtschedules");
when(command.metadata()).thenReturn(metadataBuilder.build());
final JsonObject payload = mock(JsonObject.class);
when(command.payload()).thenReturn(payload);

final ArgumentCaptor<Envelope> senderEnvelopeCaptor = forClass(Envelope.class);
listingCommandApi.handleMigrateCrownHearingsToCourtSchedules(command);

verify(sender).send(senderEnvelopeCaptor.capture());
assertThat(senderEnvelopeCaptor.getValue().metadata().name(), is("listing.command.migrate-crown-hearings-to-courtschedules"));
assertThat(senderEnvelopeCaptor.getValue().payload(), is(payload));
}

@Test
public void shouldEditNote() {
final JsonEnvelope command = mock(JsonEnvelope.class);
Expand Down
2 changes: 1 addition & 1 deletion listing-command/listing-command-handler/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<artifactId>listing-command</artifactId>
<groupId>uk.gov.moj.cpp.listing</groupId>
<version>17.104.4-SNAPSHOT</version>
<version>17.104.4-MIG-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ public class ListingCommandHandler {

public static final String HEARING_ID = "hearingId";
private static final String HEARING_DAY_COURT_SCHEDULES = "hearingDayCourtSchedules";
private static final String HEARINGS = "hearings";
private static final String PROSECUTION_CASE = "prosecutionCase";
public static final String OUCODE = "oucode";

Expand Down Expand Up @@ -1423,15 +1424,32 @@ public void correctHearingDaysWithoutCourtCentre(final JsonEnvelope commandEnvel
public void updateHearingDayCourtSchedule(final JsonEnvelope commandEnvelope) throws EventStreamException {
final JsonObject payload = commandEnvelope.payloadAsJsonObject();
final UUID hearingId = fromString(payload.getString(HEARING_ID));
final List<HearingDayCourtSchedule> hearingDayCourtSchedules = toHearingDayCourtSchedules(payload);
updateHearingEventStream(commandEnvelope,
hearingId,
hearing -> hearing.raiseHearingDayCourtSchedulesUpdated(hearingId, hearingDayCourtSchedules));
}

@Handles("listing.command.migrate-crown-hearings-to-courtschedules")
public void migrateCrownHearingsToCourtSchedules(final JsonEnvelope commandEnvelope) throws EventStreamException {
final JsonObject payload = commandEnvelope.payloadAsJsonObject();
for (final JsonObject hearing : payload.getJsonArray(HEARINGS).getValuesAs(JsonObject.class)) {
final UUID hearingId = fromString(hearing.getString(HEARING_ID));
final List<HearingDayCourtSchedule> hearingDayCourtSchedules = toHearingDayCourtSchedules(hearing);
final EventStream eventStream = eventSource.getStreamById(hearingId);
final Hearing hearingAggregate = aggregateService.get(eventStream, Hearing.class);
appendEventsToStream(commandEnvelope, eventStream,
hearingAggregate.raiseCrownHearingMigratedToCourtSchedule(hearingId, hearingDayCourtSchedules));
}
}

private List<HearingDayCourtSchedule> toHearingDayCourtSchedules(final JsonObject source) {
final List<HearingDayCourtSchedule> hearingDayCourtSchedules = new ArrayList<>();
payload.getJsonArray(HEARING_DAY_COURT_SCHEDULES)
source.getJsonArray(HEARING_DAY_COURT_SCHEDULES)
.getValuesAs(JsonObject.class)
.stream()
.forEach(hearingDayCourtSchedule -> hearingDayCourtSchedules.add(
jsonObjectConverter.convert(hearingDayCourtSchedule, HearingDayCourtSchedule.class)));
updateHearingEventStream(commandEnvelope,
hearingId,
hearing -> hearing.raiseHearingDayCourtSchedulesUpdated(hearingId, hearingDayCourtSchedules));
return hearingDayCourtSchedules;
}

@Handles("listing.command.update-cps-prosecutor-with-associated-hearings")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"hearings": [
{
"hearingId": "4182b7c6-6393-43f8-97ea-9a7e58b72ba4",
"hearingDayCourtSchedules": [
{
"hearingDate": "2026-06-22",
"courtScheduleId": "df44e775-d809-4836-a14d-5a9f7c2078c1"
}
]
},
{
"hearingId": "7c2b9e10-4d33-4a8e-9b21-0a1f5c6d7e88",
"hearingDayCourtSchedules": [
{
"hearingDate": "2026-07-14",
"courtScheduleId": "1b9a4f02-77c1-4e0a-8d3b-2c5e9f10a644"
},
{
"hearingDate": "2026-07-15",
"courtScheduleId": "2c0b5103-88d2-4f1b-9e4c-3d6fa021b755"
},
{
"hearingDate": "2026-07-16",
"courtScheduleId": "3d1c6214-99e3-401c-af5d-4e70b132c866"
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "http://justice.gov.uk/listing/courts/listing.command.migrate-crown-hearings-to-courtschedules.json",
"type": "object",
"properties": {
"hearings": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "http://justice.gov.uk/listing/courts/migrate-crown-hearing.json"
}
}
},
"required": [
"hearings"
],
"additionalProperties": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ baseUri: message://command/handler/message/listing
schema: !include json/schema/listing.command.update-hearing-day-court-schedule.json
example: !include json/listing.command.update-hearing-day-court-schedule.json

application/vnd.listing.command.migrate-crown-hearings-to-courtschedules+json:
schema: !include json/schema/listing.command.migrate-crown-hearings-to-courtschedules.json
example: !include json/listing.command.migrate-crown-hearings-to-courtschedules.json

application/vnd.listing.command.mark-hearing-as-duplicate+json:
example: !include json/listing.command.mark-hearing-as-duplicate.json
schema: !include json/schema/listing.command.mark-hearing-as-duplicate.json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4025,6 +4025,34 @@ public void shouldHandleUpdateHearingDaysWithoutCourtSchedule() throws EventStre
verify(hearing).raiseHearingDayCourtSchedulesUpdated(any(), any());
}

@Test
public void shouldMigrateCrownHearingsToCourtSchedulesForEachHearing() throws EventStreamException, IOException {
final UUID hearingId1 = randomUUID();
final UUID hearingId2 = randomUUID();
final String daysJson1 = "[{\"hearingDate\":\"2026-06-22\",\"courtScheduleId\":\"df44e775-d809-4836-a14d-5a9f7c2078c1\"}]";
final String daysJson2 = "[{\"hearingDate\":\"2026-07-14\",\"courtScheduleId\":\"1b9a4f02-77c1-4e0a-8d3b-2c5e9f10a644\"},{\"hearingDate\":\"2026-07-15\",\"courtScheduleId\":\"2c0b5103-88d2-4f1b-9e4c-3d6fa021b755\"}]";

final JsonArray hearings = createArrayBuilder()
.add(createObjectBuilder()
.add("hearingId", hearingId1.toString())
.add("hearingDayCourtSchedules", objectMapper.readValue(daysJson1, JsonArray.class)))
.add(createObjectBuilder()
.add("hearingId", hearingId2.toString())
.add("hearingDayCourtSchedules", objectMapper.readValue(daysJson2, JsonArray.class)))
.build();
final JsonObject payload = createObjectBuilder().add("hearings", hearings).build();

final JsonEnvelope commandEnvelope = envelopeFrom(metadataWithRandomUUID("listing.command.migrate-crown-hearings-to-courtschedules"), payload);
when(eventSource.getStreamById(any(UUID.class))).thenReturn(eventStream);

listingCommandHandler.migrateCrownHearingsToCourtSchedules(commandEnvelope);

verify(eventSource).getStreamById(hearingId1);
verify(eventSource).getStreamById(hearingId2);
verify(hearing, times(2)).raiseCrownHearingMigratedToCourtSchedule(any(), any());
verify(eventStream, times(2)).append(any());
}

private void shouldRequestPublicationOfACourtListForAllCrownCourtsAsExpected(final LocalDate dayOfRequest, final LocalDate expectedListingDate) {

fixClock(dayOfRequest.atStartOfDay().toInstant(ZoneOffset.UTC));
Expand Down
2 changes: 1 addition & 1 deletion listing-command/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<artifactId>listing-parent</artifactId>
<groupId>uk.gov.moj.cpp.listing</groupId>
<version>17.104.4-SNAPSHOT</version>
<version>17.104.4-MIG-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
2 changes: 1 addition & 1 deletion listing-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<artifactId>listing-parent</artifactId>
<groupId>uk.gov.moj.cpp.listing</groupId>
<version>17.104.4-SNAPSHOT</version>
<version>17.104.4-MIG-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
2 changes: 1 addition & 1 deletion listing-domain/listing-domain-aggregate/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<artifactId>listing-domain</artifactId>
<groupId>uk.gov.moj.cpp.listing</groupId>
<version>17.104.4-SNAPSHOT</version>
<version>17.104.4-MIG-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
Loading
Loading