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
12 changes: 12 additions & 0 deletions hearing-command/hearing-command-handler/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,20 @@
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>

<!-- Test Dependencies -->

<dependency>
<groupId>uk.gov.justice.utils</groupId>
<artifactId>test-utils-logging-log4j</artifactId>
<type>pom</type>
<scope>test</scope>
</dependency>

<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>test-utils-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@
import static java.util.UUID.fromString;
import static uk.gov.justice.services.core.annotation.Component.COMMAND_HANDLER;

import uk.gov.justice.core.courts.HearingDay;
import uk.gov.justice.services.core.annotation.Handles;
import uk.gov.justice.services.core.annotation.ServiceComponent;
import uk.gov.justice.services.eventsourcing.source.core.exception.EventStreamException;
import uk.gov.justice.services.messaging.JsonEnvelope;
import uk.gov.moj.cpp.hearing.command.handler.service.ReferenceDataService;
import uk.gov.moj.cpp.hearing.command.logEvent.CorrectLogEventCommand;
import uk.gov.moj.cpp.hearing.command.logEvent.CreateHearingEventDefinitionsCommand;
import uk.gov.moj.cpp.hearing.command.logEvent.LogEventCommand;
import uk.gov.moj.cpp.hearing.command.updateEvent.UpdateHearingEventsCommand;
import uk.gov.moj.cpp.hearing.domain.CourtCentre;
import uk.gov.moj.cpp.hearing.domain.aggregate.HearingAggregate;
import uk.gov.moj.cpp.hearing.domain.aggregate.HearingEventDefinitionAggregate;
import uk.gov.moj.cpp.hearing.eventlog.HearingEvent;

import java.time.ZonedDateTime;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import javax.inject.Inject;
import javax.json.JsonArray;
import javax.json.JsonObject;

Expand All @@ -38,6 +44,9 @@ public class HearingEventCommandHandler extends AbstractCommandHandler {

private static final UUID PAUSE_HEARING_EVENT_DEFINITION_ID = UUID.fromString("160ecb51-29ee-4954-bbbf-daab18a24fbb");

@Inject
private ReferenceDataService referenceDataService;

@Handles("hearing.create-hearing-event-definitions")
public void createHearingEventDefinitions(final JsonEnvelope jsonEnvelope) throws EventStreamException {
if (LOGGER.isDebugEnabled()) {
Expand Down Expand Up @@ -94,12 +103,14 @@ public void logHearingEvent(final JsonEnvelope jsonEnvelope) throws EventStreamE
.build();

final UUID activeHearingId = UUID.fromString(activeHearings.getString(index));
final CourtCentre pauseCourtCentre = resolveCourtCentre(activeHearingId, logEventCommand.getEventTime());

aggregate(HearingAggregate.class, activeHearingId, jsonEnvelope, a -> a.logHearingEvent(activeHearingId, PAUSE_HEARING_EVENT_DEFINITION_ID, alterable, defenceCounselId, pauseHearingEvent, hearingTypeIds, userId));
aggregate(HearingAggregate.class, activeHearingId, jsonEnvelope, a -> a.logHearingEvent(activeHearingId, PAUSE_HEARING_EVENT_DEFINITION_ID, alterable, defenceCounselId, pauseHearingEvent, hearingTypeIds, userId, pauseCourtCentre));
}
}

aggregate(HearingAggregate.class, logEventCommand.getHearingId(), jsonEnvelope, a -> a.logHearingEvent(hearingId, hearingEventDefinitionId, alterable, defenceCounselId, hearingEvent, hearingTypeIds, userId));
final CourtCentre courtCentre = resolveCourtCentre(hearingId, logEventCommand.getEventTime());
aggregate(HearingAggregate.class, logEventCommand.getHearingId(), jsonEnvelope, a -> a.logHearingEvent(hearingId, hearingEventDefinitionId, alterable, defenceCounselId, hearingEvent, hearingTypeIds, userId, courtCentre));
}

@Handles("hearing.command.update-hearing-events")
Expand Down Expand Up @@ -130,12 +141,33 @@ public void correctEvent(final JsonEnvelope jsonEnvelope) throws EventStreamExce
.withLastModifiedTime(correctLogEventCommand.getLastModifiedTime())
.withRecordedLabel(correctLogEventCommand.getRecordedLabel())
.withNote(correctLogEventCommand.getNote()).build();
final CourtCentre courtCentre = resolveCourtCentre(correctLogEventCommand.getHearingId(), correctLogEventCommand.getEventTime());
aggregate(HearingAggregate.class, correctLogEventCommand.getHearingId(), jsonEnvelope, a -> a.correctHearingEvent(correctLogEventCommand.getLatestHearingEventId(),
correctLogEventCommand.getHearingId(),
correctLogEventCommand.getHearingEventDefinitionId(),
correctLogEventCommand.getAlterable(),
correctLogEventCommand.getDefenceCounselId(),
hearingEvent,
userId));
userId,
courtCentre));
}

private CourtCentre resolveCourtCentre(final UUID hearingId, final ZonedDateTime eventTime) {
try {
final HearingAggregate aggregate = aggregate(HearingAggregate.class, hearingId);
final Optional<HearingDay> matchedDay = aggregate.findHearingDayFor(eventTime);
if (matchedDay.isEmpty()) {
return null;
}
final UUID centreId = matchedDay.get().getCourtCentreId();
final UUID roomId = matchedDay.get().getCourtRoomId();
if (centreId == null || roomId == null) {
return null;
}
return referenceDataService.resolveCourtCentre(centreId, roomId).orElse(null);
} catch (Exception e) {
LOGGER.warn("Failed to resolve court centre for hearing {} at {}; falling back to top-level", hearingId, eventTime, e);
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,20 @@
import uk.gov.moj.cpp.hearing.command.result.SharedResultsCommandResultLineV2;
import uk.gov.moj.cpp.hearing.command.result.UpdateDaysResultLinesStatusCommand;
import uk.gov.moj.cpp.hearing.command.result.UpdateResultLinesStatusCommand;
import uk.gov.moj.cpp.hearing.command.handler.service.validation.ResultsValidator;
import uk.gov.moj.cpp.hearing.command.handler.service.validation.ValidationRequest;
import uk.gov.moj.cpp.hearing.command.handler.service.validation.ValidationRequestMapper;
import uk.gov.moj.cpp.hearing.command.handler.service.validation.ValidationResponse;
import uk.gov.moj.cpp.hearing.domain.aggregate.ApplicationAggregate;
import uk.gov.moj.cpp.hearing.domain.aggregate.HearingAggregate;
import uk.gov.moj.cpp.hearing.domain.event.result.ResultsValidationFailed;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.inject.Inject;
Expand All @@ -56,6 +60,12 @@ public class ShareResultsCommandHandler extends AbstractCommandHandler {
@Inject
private ReferenceDataService referenceDataService;

@Inject
private ResultsValidator resultsValidationClient;

@Inject
private ValidationRequestMapper validationRequestMapper;


@Handles("hearing.command.save-draft-result")
public void saveDraftResult(final JsonEnvelope envelope) throws EventStreamException {
Expand Down Expand Up @@ -174,10 +184,28 @@ public void shareResultForDay(final JsonEnvelope envelope) throws EventStreamExc
}
final ShareDaysResultsCommand command = convertToObject(envelope, ShareDaysResultsCommand.class);
final UUID userId = envelope.metadata().userId().map(UUID::fromString).orElse(null);
long start = System.currentTimeMillis();

final EventStream eventStream = eventSource.getStreamById(command.getHearingId());
final HearingAggregate hearingAggregate = aggregateService.get(eventStream, HearingAggregate.class);

aggregate(HearingAggregate.class, command.getHearingId(), envelope,
aggregate -> shareDaysResultsEnrichedWithYouthCourt(aggregate, command, userId));
final ValidationRequest validationRequest = validationRequestMapper.toValidationRequest(command, hearingAggregate.getHearing());
final String userIdString = userId != null ? userId.toString() : "";

final ValidationResponse validationResponse = resultsValidationClient.validate(validationRequest, userIdString);
long end = System.currentTimeMillis();
LOGGER.info("Validation API call took {} ms for userId={} and for hearingId={}", end - start, userIdString, validationRequest.getHearingId());

if (validationResponse.hasErrors()) {
LOGGER.info("Share blocked by validation errors for hearing {}", command.getHearingId());
final ResultsValidationFailed failedEvent = buildValidationFailedEvent(command, userIdString, validationResponse);
eventStream.append(Stream.of(failedEvent).map(enveloper.withMetadataFrom(envelope)));
return;
}

eventStream.append(
shareDaysResultsEnrichedWithYouthCourt(hearingAggregate, command, userId)
.map(enveloper.withMetadataFrom(envelope)));
}

@Handles("hearing.command.update-result-lines-status")
Expand Down Expand Up @@ -215,6 +243,30 @@ public void replicateAllSharedResultsForHearing(final JsonEnvelope envelope) thr
aggregate -> aggregate.replicateSharedResultsForHearing(hearingId));
}

private ResultsValidationFailed buildValidationFailedEvent(final ShareDaysResultsCommand command,
final String userId,
final ValidationResponse validationResponse) {
return ResultsValidationFailed.builder()
.withHearingId(command.getHearingId())
.withHearingDay(command.getHearingDay())
.withUserId(userId)
.withErrors(validationResponse.getErrors().stream()
.map(e -> new ResultsValidationFailed.ValidationError(
e.getRuleId(), e.getSeverity(), e.getMessage(),
e.getAffectedOffences().stream()
.map(o -> o.getId())
.toList()))
.toList())
.withWarnings(validationResponse.getWarnings().stream()
.map(w -> new ResultsValidationFailed.ValidationError(
w.getRuleId(), w.getSeverity(), w.getMessage(),
w.getAffectedOffences().stream()
.map(o -> o.getId())
.toList()))
.toList())
.build();
}

private Stream<Object> shareResultsEnrichedWithYouthCourt(final HearingAggregate hearingAggregate, final ShareResultsCommand command ) {

final Hearing hearing = hearingAggregate.getHearing();
Expand Down Expand Up @@ -278,7 +330,7 @@ List<CourtApplication> getAdditionalApplications(final Set<UUID> distinctApplica
return null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
.toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
import uk.gov.justice.services.messaging.Envelope;
import uk.gov.justice.services.messaging.JsonEnvelope;
import uk.gov.justice.services.messaging.MetadataBuilder;
import uk.gov.moj.cpp.hearing.domain.CourtCentre;

import javax.inject.Inject;
import javax.json.JsonArray;
import javax.json.JsonObject;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

import static java.util.Objects.isNull;
import static java.util.UUID.randomUUID;
import static uk.gov.justice.services.messaging.JsonObjects.createObjectBuilder;
import static uk.gov.justice.services.core.annotation.Component.COMMAND_API;
Expand Down Expand Up @@ -129,6 +132,72 @@ public void setId(UUID id) {
}


public Optional<CourtCentre> resolveCourtCentre(final UUID courtCentreId, final UUID courtRoomId) {
if (courtCentreId == null || courtRoomId == null) {
return Optional.empty();
}

final JsonArray organisationUnits = queryCourtCentresFor(courtCentreId);
if (organisationUnits == null || organisationUnits.isEmpty()) {
return Optional.empty();
}

return findMatchingOrganisationUnit(organisationUnits, courtCentreId)
.flatMap(ou -> buildCourtCentreFromRoom(ou, courtCentreId, courtRoomId));
}

private JsonArray queryCourtCentresFor(final UUID courtCentreId) {
final JsonObject payload = createObjectBuilder()
.add("courtCentreId", courtCentreId.toString())
.build();

final JsonEnvelope requestEnvelope = envelopeFrom(
metadataBuilder()
.withName(REFERENCEDATA_QUERY_COURT_CENTRES)
.withId(randomUUID())
.build(),
payload);

return requester.requestAsAdmin(requestEnvelope)
.payloadAsJsonObject()
.getJsonArray("organisationunits");
}

private Optional<JsonObject> findMatchingOrganisationUnit(final JsonArray organisationUnits, final UUID courtCentreId) {
final String idAsString = courtCentreId.toString();
return organisationUnits.getValuesAs(JsonObject.class).stream()
.filter(unit -> idAsString.equals(getStringOrNull(unit, "id")))
.findFirst();
}

private Optional<CourtCentre> buildCourtCentreFromRoom(final JsonObject ou, final UUID courtCentreId, final UUID courtRoomId) {
final JsonArray courtrooms = ou.getJsonArray("courtrooms");
if (isNull(courtrooms)) {
return Optional.empty();
}

return courtrooms.getValuesAs(JsonObject.class).stream()
.filter(room -> roomIdMatches(room, courtRoomId))
.findFirst()
.map(room -> CourtCentre.courtCentre()
.withId(courtCentreId)
.withName(getStringOrNull(ou, "oucodeL3Name"))
.withWelshName(getStringOrNull(ou, "oucodeL3WelshName"))
.withRoomId(courtRoomId)
.withRoomName(getStringOrNull(room, "courtroomName"))
.withWelshRoomName(getStringOrNull(room, "welshCourtroomName"))
.build());
}

private static boolean roomIdMatches(final JsonObject room, final UUID courtRoomId) {
final String id = getStringOrNull(room, "id");
return id != null && courtRoomId.equals(UUID.fromString(id));
}

private static String getStringOrNull(final JsonObject json, final String key) {
return json.containsKey(key) ? json.getString(key) : null;
}

public Set<String> retrieveGuiltyPleaTypes() {
final MetadataBuilder metadataBuilder = metadataBuilder()
.withId(randomUUID())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package uk.gov.moj.cpp.hearing.command.handler.service.validation;

public class DefendantDto {
private final String id;
private final String firstName;
private final String lastName;
private final String masterDefendantId;

private DefendantDto(Builder builder) {
this.id = builder.id;
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.masterDefendantId = builder.masterDefendantId;
}

public String getId() {
return id;
}

public String getFirstName() {
return firstName;
}

public String getLastName() {
return lastName;
}

public String getMasterDefendantId() {
return masterDefendantId;
}

public static Builder builder() {
return new Builder();
}

public static final class Builder {
private String id;
private String firstName;
private String lastName;
private String masterDefendantId;

public Builder withId(String id) {
this.id = id;
return this;
}

public Builder withFirstName(String firstName) {
this.firstName = firstName;
return this;
}

public Builder withLastName(String lastName) {
this.lastName = lastName;
return this;
}

public Builder withMasterDefendantId(String masterDefendantId) {
this.masterDefendantId = masterDefendantId;
return this;
}

public DefendantDto build() {
return new DefendantDto(this);
}
}
}
Loading
Loading