From dc31ea4a2a4c7a02b697936d79d0461355b12f01 Mon Sep 17 00:00:00 2001 From: Sivarama Varma Penmetsa Date: Tue, 16 Jun 2026 13:22:30 +0100 Subject: [PATCH 01/22] initial commit --- .specify/feature.json | 3 + CLAUDE.md | 5 + .../results.add-informant-register.json | 2 +- .../informantRegisterCaseOrApplication.json | 38 +++ .../informantRegisterDefendant.json | 73 +++++ .../informantRegisterDocumentRequest.json | 67 ++++ .../informantRegisterHearing.json | 30 ++ .../informantRegisterHearingVenue.json | 28 ++ .../informantRegisterOffence.json | 46 +++ .../informantRegisterRecipient.json | 26 ++ .../informantRegisterResult.json | 23 ++ .../informantRegisterResultData.json | 40 +++ .../prosecutorResult.json | 47 +++ .../informantRegisterDocument/verdict.json | 21 ++ ...event.informant-register-generated-v2.json | 18 ++ ....event.informant-register-recorded-v2.json | 20 ++ .../src/yaml/subscriptions-descriptor.yaml | 6 + .../src/yaml/subscriptions-descriptor.yaml | 2 + .../schema/results.prosecutor-results.json | 2 +- .../checklists/requirements.md | 36 +++ .../contracts/event-contracts.md | 228 +++++++++++++ .../data-model.md | 196 ++++++++++++ .../plan.md | 299 ++++++++++++++++++ .../quickstart.md | 71 +++++ .../research.md | 107 +++++++ .../spec.md | 155 +++++++++ .../tasks.md | 250 +++++++++++++++ 27 files changed, 1837 insertions(+), 2 deletions(-) create mode 100644 .specify/feature.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/verdict.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-generated-v2.json create mode 100644 results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json create mode 100644 specs/001-informant-register-local-schema/checklists/requirements.md create mode 100644 specs/001-informant-register-local-schema/contracts/event-contracts.md create mode 100644 specs/001-informant-register-local-schema/data-model.md create mode 100644 specs/001-informant-register-local-schema/plan.md create mode 100644 specs/001-informant-register-local-schema/quickstart.md create mode 100644 specs/001-informant-register-local-schema/research.md create mode 100644 specs/001-informant-register-local-schema/spec.md create mode 100644 specs/001-informant-register-local-schema/tasks.md diff --git a/.specify/feature.json b/.specify/feature.json new file mode 100644 index 000000000..c0de09054 --- /dev/null +++ b/.specify/feature.json @@ -0,0 +1,3 @@ +{ + "feature_directory": "specs/001-informant-register-local-schema" +} diff --git a/CLAUDE.md b/CLAUDE.md index 38922dfe3..079c6e8c1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -134,4 +134,9 @@ This repo uses Spec Kit. For non-trivial work use: For additional context about technologies to be used, project structure, shell commands, and other important information, read the current plan + +**Active feature**: CIMD-3915 — Include Verdict in SJP Results +**Branch**: `CIMD-3915-informant-register-local-schema` +**Implementation plan**: [specs/001-informant-register-local-schema/plan.md](specs/001-informant-register-local-schema/plan.md) +**Quickstart**: [specs/001-informant-register-local-schema/quickstart.md](specs/001-informant-register-local-schema/quickstart.md) diff --git a/results-command/results-command-api/src/raml/json/schema/results.add-informant-register.json b/results-command/results-command-api/src/raml/json/schema/results.add-informant-register.json index 1407cdea5..fa19d611f 100644 --- a/results-command/results-command-api/src/raml/json/schema/results.add-informant-register.json +++ b/results-command/results-command-api/src/raml/json/schema/results.add-informant-register.json @@ -1,5 +1,5 @@ { "$schema": "http://json-schema.org/draft-04/schema#", "id": "http://moj.gov.uk/cpp/results/command/add-informant-register-request.json", - "$ref": "http://justice.gov.uk/core/courts/informantRegisterDocument/informantRegisterDocumentRequest.json" + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json" } \ No newline at end of file diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json new file mode 100644 index 000000000..35b2ff085 --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterCaseOrApplication.json", + "description": "The details of a defendants prosecution case or application rendered in a informant register document", + "type": "object", + "properties": { + "caseOrApplicationReference": { + "description": "The case or application reference", + "type": "string" + }, + "arrestSummonsNumber": { + "type": "string" + }, + "applicationParticulars": { + "type": "string" + }, + "offences": { + "description": "The details of the offences for the defendant", + "type": "array", + "minItems": 1, + "items": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterOffence.json" + } + }, + "results": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterResult.json" + } + } + }, + "required": [ + "caseOrApplicationReference", + "offences" + ], + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json new file mode 100644 index 000000000..cf7a4c9aa --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json @@ -0,0 +1,73 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDefendant.json", + "description": "The details of a defendant rendered in a informant register document", + "type": "object", + "properties": { + "name": { + "description": "The defendant name", + "type": "string" + }, + "dateOfBirth": { + "description": "The defendant date of birth", + "type": "string", + "format": "date" + }, + "address1": { + "description": "The defendant address line 1", + "type": "string" + }, + "address2": { + "description": "The defendant address line 2", + "type": "string" + }, + "address3": { + "description": "The defendant address line 3", + "type": "string" + }, + "address4": { + "description": "The defendant address line 4", + "type": "string" + }, + "address5": { + "description": "The defendant address line 5", + "type": "string" + }, + "postCode": { + "description": "The defendant postCode", + "type": "string" + }, + "nationality": { + "description": "The defendant nationality", + "type": "string" + }, + "title": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "prosecutionCasesOrApplications": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterCaseOrApplication.json" + } + }, + "results": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterResult.json" + } + } + }, + "required": [ + "name", + "address1" + ], + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json new file mode 100644 index 000000000..096930c91 --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json", + "description": "The details required to batch and generate a informant register document. Batching is by date, prosecuting authority id, court Centre Id, hearing Id", + "type": "object", + "properties": { + "registerDate": { + "type": "string", + "format": "date-time" + }, + "hearingDate": { + "type": "string", + "format": "date-time" + }, + "hearingId": { + "description": "The hearing id of the hearing shared", + "type": "string" + }, + "prosecutionAuthorityId": { + "description": "The prosecuting authority reference data identifier", + "type": "string" + }, + "prosecutionAuthorityCode": { + "description": "The prosecuting authority reference data code", + "type": "string" + }, + "prosecutionAuthorityOuCode": { + "description": "The prosecuting authority reference data organisation unit code", + "type": "string" + }, + "majorCreditorCode": { + "type": "string" + }, + "prosecutionAuthorityName": { + "type": "string", + "description": "The name of the prosecution authority from reference data. Required for documentation purposes." + }, + "fileName": { + "type": "string" + }, + "recipients": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterRecipient.json" + } + }, + "hearingVenue": { + "description": "The venue for the hearings for the prosecuting authority in question", + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterHearingVenue.json" + }, + "groupId": { + "description": "The group id of the group cases", + "type": "string" + } + }, + "required": [ + "registerDate", + "hearingId", + "hearingDate", + "prosecutionAuthorityId", + "prosecutionAuthorityCode", + "hearingVenue", + "fileName" + ], + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json new file mode 100644 index 000000000..542369109 --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterHearing.json", + "description": "The hearing details for the informant register entry", + "type": "object", + "properties": { + "courtRoom": { + "description": "The court room where the hearing was proceeded", + "type": "string" + }, + "hearingStartTime": { + "description": "The start time of the hearing", + "type": "string", + "format": "time" + }, + "defendants": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDefendant.json" + } + } + }, + "required": [ + "courtRoom", + "hearingStartTime", + "defendants" + ], + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json new file mode 100644 index 000000000..aa47bed4c --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterHearingVenue.json", + "description": "The details of a hearing venue rendered in a informant register", + "type": "object", + "properties": { + "ljaName": { + "description": "The court venue local justice area Name", + "type": "string" + }, + "courtHouse": { + "description": "The court venue Name", + "type": "string" + }, + "courtSessions": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterHearing.json" + } + } + }, + "required": [ + "courtHouse", + "courtSessions" + ], + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json new file mode 100644 index 000000000..87ffa5a1a --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json @@ -0,0 +1,46 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterOffence.json", + "description": "The details of a defendant offence for a prosecution case rendered in a informant register document", + "type": "object", + "properties": { + "originatingCaseUrn": { + "description": "URN of the case containing the offence", + "type": "string" + }, + "offenceCode": { + "description": "The cjs code for the offence", + "type": "string" + }, + "orderIndex": { + "description": "The offence index", + "type": "integer", + "minimum": 0 + }, + "offenceTitle": { + "description": "The title taken from ref data", + "type": "string" + }, + "pleaValue": { + "description": "The defendants plea against the offence", + "type": "string" + }, + "verdict": { + "description": "The structured verdict recorded against the offence", + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/verdict.json" + }, + "offenceResults": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterResult.json" + } + } + }, + "required": [ + "offenceCode", + "orderIndex", + "offenceTitle" + ], + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json new file mode 100644 index 000000000..b1bc186eb --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterRecipient.json", + "description": "The recipient details for the Informant Register document", + "type": "object", + "properties": { + "recipientName": { + "type": "string" + }, + "emailAddress1": { + "type": "string" + }, + "emailAddress2": { + "type": "string" + }, + "emailTemplateName": { + "type": "string" + } + }, + "required": [ + "recipientName", + "emailAddress1", + "emailTemplateName" + ], + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json new file mode 100644 index 000000000..ea03220d8 --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterResult.json", + "description": "The details of a published result rendered in a informant register document", + "type": "object", + "properties": { + "resultText": { + "description": "The textual description of the result", + "type": "string" + }, + "cjsResultCode": { + "description": "The cjs code for the result", + "type": "string" + }, + "resultData": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterResultData.json" + } + }, + "required": [ + "resultText" + ], + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json new file mode 100644 index 000000000..df69aad5f --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterResultData.json", + "description": "The details of a published result rendered in a informant register document", + "type": "object", + "properties": { + "amount": { + "description": "", + "type": "string" + }, + "nextHearingDate": { + "type": "string", + "format": "date-time" + }, + "nextCourtLocation": { + "type": "string" + }, + "durationValue": { + "type": "string" + }, + "durationUnit": { + "type": "string" + }, + "durationStartDate": { + "type": "string", + "format": "date-time" + }, + "durationEndDate": { + "type": "string", + "format": "date-time" + }, + "secondaryDurationValue": { + "type": "string" + }, + "secondaryDurationUnit": { + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json new file mode 100644 index 000000000..eae865a4b --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/prosecutorResult.json", + "description": "Response structure for results returned to prosecutor", + "type": "object", + "properties": { + "startDate": { + "type": "string", + "format": "date" + }, + "endDate": { + "type": "string", + "format": "date" + }, + "prosecutionAuthorityId": { + "description": "The prosecuting authority reference data identifier", + "type": "string" + }, + "prosecutionAuthorityCode": { + "description": "The prosecuting authority reference data code", + "type": "string" + }, + "prosecutionAuthorityName": { + "type": "string", + "description": "The name of the prosecution authority from reference data." + }, + "prosecutionAuthorityOuCode": { + "type": "string" + }, + "majorCreditorCode": { + "type": "string" + }, + "hearingVenues": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterHearingVenue.json" + } + } + }, + "required": [ + "startDate", + "prosecutionAuthorityId", + "prosecutionAuthorityCode" + ], + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/verdict.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/verdict.json new file mode 100644 index 000000000..33981af27 --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/verdict.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/verdict.json", + "description": "Verdict recorded against an SJP offence", + "type": "object", + "properties": { + "verdictCode": { + "description": "Verdict code: G (FOUND_GUILTY), N (FOUND_NOT_GUILTY), PSJ (PROVED_SJP)", + "type": "string" + }, + "verdictDate": { + "description": "Conviction date in yyyy-MM-dd format. Mandatory when verdictCode is present.", + "type": "string" + }, + "verdictType": { + "description": "Verdict type resolved from reference data: FOUND_GUILTY, FOUND_NOT_GUILTY, PROVED_SJP", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-generated-v2.json b/results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-generated-v2.json new file mode 100644 index 000000000..8c8144a5f --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-generated-v2.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informant-register-generated-v2.json", + "description": "Event schema for generating informant register document request with structured verdict", + "type": "object", + "properties": { + "informantRegisterDocumentRequests": { + "type": "array", + "items": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json" + } + }, + "systemGenerated": { + "type": "boolean" + } + }, + "additionalProperties": false +} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json b/results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json new file mode 100644 index 000000000..c63cb6ae4 --- /dev/null +++ b/results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informant-register-recorded-v2.json", + "description": "Event schema for recording informant register document request with structured verdict", + "type": "object", + "properties": { + "prosecutionAuthorityId": { + "description": "The prosecuting authority reference data identifier", + "type": "string" + }, + "informantRegister": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json" + } + }, + "required": [ + "prosecutionAuthorityId", + "informantRegister" + ], + "additionalProperties": false +} diff --git a/results-event/results-event-listener/src/yaml/subscriptions-descriptor.yaml b/results-event/results-event-listener/src/yaml/subscriptions-descriptor.yaml index 522f0777f..b7b321076 100644 --- a/results-event/results-event-listener/src/yaml/subscriptions-descriptor.yaml +++ b/results-event/results-event-listener/src/yaml/subscriptions-descriptor.yaml @@ -23,6 +23,12 @@ subscriptions_descriptor: - name: results.event.informant-register-generated schema_uri: http://justice.gov.uk/results/courts/informant-register-generated.json + - name: results.event.informant-register-recorded-v2 + schema_uri: http://justice.gov.uk/results/courts/informant-register-recorded-v2.json + + - name: results.event.informant-register-generated-v2 + schema_uri: http://justice.gov.uk/results/courts/informant-register-generated-v2.json + - name: results.event.informant-register-notified schema_uri: http://justice.gov.uk/results/courts/informant-register-notified.json diff --git a/results-event/results-event-processor/src/yaml/subscriptions-descriptor.yaml b/results-event/results-event-processor/src/yaml/subscriptions-descriptor.yaml index 7d13da3f1..850ddccc7 100644 --- a/results-event/results-event-processor/src/yaml/subscriptions-descriptor.yaml +++ b/results-event/results-event-processor/src/yaml/subscriptions-descriptor.yaml @@ -27,6 +27,8 @@ subscriptions_descriptor: schema_uri: http://justice.gov.uk/core/courts/results.event.nces-email-notification.json - name: results.event.informant-register-generated schema_uri: http://justice.gov.uk/results/courts/informant-register-generated.json + - name: results.event.informant-register-generated-v2 + schema_uri: http://justice.gov.uk/results/courts/informant-register-generated-v2.json - name: results.event.informant-register-notified schema_uri: http://justice.gov.uk/results/courts/informant-register-notified.json - name: results.event.appeal-update-notification-requested diff --git a/results-query/results-query-api/src/raml/json/schema/results.prosecutor-results.json b/results-query/results-query-api/src/raml/json/schema/results.prosecutor-results.json index dfe1e7600..6b6fdb4fe 100644 --- a/results-query/results-query-api/src/raml/json/schema/results.prosecutor-results.json +++ b/results-query/results-query-api/src/raml/json/schema/results.prosecutor-results.json @@ -3,5 +3,5 @@ "description": "Response schema for prosecutor results", "id": "http://justice.gov.uk/results/courts/informant-register/prosecutor-result.json", "type": "object", - "$ref": "http://justice.gov.uk/core/courts/informantRegisterDocument/prosecutorResult.json" + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/prosecutorResult.json" } \ No newline at end of file diff --git a/specs/001-informant-register-local-schema/checklists/requirements.md b/specs/001-informant-register-local-schema/checklists/requirements.md new file mode 100644 index 000000000..8ac107f04 --- /dev/null +++ b/specs/001-informant-register-local-schema/checklists/requirements.md @@ -0,0 +1,36 @@ +# Specification Quality Checklist: Include Verdict in SJP Results — results context + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-06-16 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) +- [x] Focused on user value and business needs +- [x] Written for non-technical stakeholders +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic (no implementation details) +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded (results context only) +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover all primary flows within this context +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] No implementation details leak into specification + +## Notes + +- Scope trimmed to cpp-context-results only per team instruction (2026-06-16). +- All 8 ACs from CIMD-3915 are represented within the results context boundary. +- All items pass. Spec is ready for `/speckit-plan`. diff --git a/specs/001-informant-register-local-schema/contracts/event-contracts.md b/specs/001-informant-register-local-schema/contracts/event-contracts.md new file mode 100644 index 000000000..141829034 --- /dev/null +++ b/specs/001-informant-register-local-schema/contracts/event-contracts.md @@ -0,0 +1,228 @@ +# Event Contracts: Include Verdict in SJP Results + +**Date**: 2026-06-16 | **Branch**: `CIMD-3915-informant-register-local-schema` + +## New Domain Events + +### `results.event.informant-register-recorded-v2` + +Emitted by: `InformantRegisterHandler.handleAddInformantRegisterToEventStream` +Schema `$id`: `http://justice.gov.uk/results/courts/informant-register-recorded-v2.json` +Schema file: `results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json` +Subscription (listener): added to `results-event-listener/src/yaml/subscriptions-descriptor.yaml` +Subscription (processor): NOT subscribed (processor does not need to react to recorded V2) + +```json +{ + "prosecutionAuthorityId": "", + "informantRegister": { + "registerDate": "2026-04-13T10:00:00.000Z", + "hearingDate": "2026-04-13T09:00:00.000Z", + "hearingId": "", + "prosecutionAuthorityId": "", + "prosecutionAuthorityCode": "DVSA", + "fileName": "informant-register-dvsa.csv", + "hearingVenue": { + "courtHouse": "Westminster Magistrates Court", + "courtSessions": [ + { + "courtRoom": "1", + "hearingStartTime": "09:00", + "defendants": [ + { + "name": "John Smith", + "address1": "1 Main Street", + "prosecutionCasesOrApplications": [ + { + "caseOrApplicationReference": "EARL001390", + "offences": [ + { + "offenceCode": "SF75060", + "offenceTitle": "Speed in excess of limit", + "orderIndex": 1, + "pleaValue": "GUILTY", + "verdict": { + "verdictCode": "G", + "verdictDate": "2026-04-13", + "verdictType": "FOUND_GUILTY" + }, + "offenceResults": [ + { + "resultText": "Fine", + "cjsResultCode": "1015" + } + ] + } + ] + } + ] + } + ] + } + ] + } + } +} +``` + +--- + +### `results.event.informant-register-generated-v2` + +Emitted by: `InformantRegisterHandler.processRequests` (called from both generate handlers) +Schema `$id`: `http://justice.gov.uk/results/courts/informant-register-generated-v2.json` +Schema file: `results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-generated-v2.json` +Subscription (listener): added to `results-event-listener/src/yaml/subscriptions-descriptor.yaml` +Subscription (processor): added to `results-event-processor/src/yaml/subscriptions-descriptor.yaml` + +```json +{ + "informantRegisterDocumentRequests": [ + { + "registerDate": "2026-04-13T10:00:00.000Z", + "hearingDate": "2026-04-13T09:00:00.000Z", + "hearingId": "", + "prosecutionAuthorityId": "", + "prosecutionAuthorityCode": "DVSA", + "fileName": "informant-register-dvsa.csv", + "hearingVenue": { "...": "same structure as above" } + } + ], + "systemGenerated": false +} +``` + +--- + +## Modified Command Schema + +### `results.add-informant-register` (command) + +Schema file: `results-command/results-command-api/src/raml/json/schema/results.add-informant-register.json` + +**Before** (delegates entirely to core-domain): +```json +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://moj.gov.uk/cpp/results/command/add-informant-register-request.json", + "$ref": "http://justice.gov.uk/core/courts/informantRegisterDocument/informantRegisterDocumentRequest.json" +} +``` + +**After** (references local schema): +```json +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://moj.gov.uk/cpp/results/command/add-informant-register-request.json", + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json" +} +``` + +--- + +## Modified Query Schema + +### `results.prosecutor-results` (query response) + +Schema file: `results-query/results-query-api/src/raml/json/schema/results.prosecutor-results.json` + +**Before** (delegates entirely to core-domain): +```json +{ + "$ref": "http://justice.gov.uk/core/courts/informantRegisterDocument/prosecutorResult.json" +} +``` + +**After** (references local schema with verdict): +```json +{ + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/prosecutorResult.json" +} +``` + +The local `prosecutorResult.json` transitively includes the local `informantRegisterOffence.json` which has the `verdict` object. + +--- + +## Subscription Descriptor Changes + +### Listener (`results-event-listener/src/yaml/subscriptions-descriptor.yaml`) + +Add after existing `results.event.informant-register-generated` entry: +```yaml +- name: results.event.informant-register-recorded-v2 + schema_uri: http://justice.gov.uk/results/courts/informant-register-recorded-v2.json + +- name: results.event.informant-register-generated-v2 + schema_uri: http://justice.gov.uk/results/courts/informant-register-generated-v2.json +``` + +### Processor (`results-event-processor/src/yaml/subscriptions-descriptor.yaml`) + +Add after existing `results.event.informant-register-generated` entry: +```yaml +- name: results.event.informant-register-generated-v2 + schema_uri: http://justice.gov.uk/results/courts/informant-register-generated-v2.json +``` + +--- + +## Verdict Object Schema (`verdict.json`) + +```json +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/verdict.json", + "description": "Verdict recorded against an SJP offence", + "type": "object", + "properties": { + "verdictCode": { + "description": "Verdict code: G (FOUND_GUILTY), N (FOUND_NOT_GUILTY), PSJ (PROVED_SJP)", + "type": "string" + }, + "verdictDate": { + "description": "Conviction date in yyyy-MM-dd format. Mandatory when verdictCode is present.", + "type": "string" + }, + "verdictType": { + "description": "Verdict type resolved from reference data: FOUND_GUILTY, FOUND_NOT_GUILTY, PROVED_SJP", + "type": "string" + } + }, + "additionalProperties": false +} +``` + +--- + +## Offence Schema Change (`informantRegisterOffence.json` — local) + +**Key change**: `verdictCode: String` → `verdict: { verdictCode, verdictDate, verdictType }` + +```json +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterOffence.json", + "description": "The details of a defendant offence for an informant register document", + "type": "object", + "properties": { + "originatingCaseUrn": { "type": "string" }, + "offenceCode": { "type": "string" }, + "orderIndex": { "type": "integer", "minimum": 0 }, + "offenceTitle": { "type": "string" }, + "pleaValue": { "type": "string" }, + "verdict": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/verdict.json" + }, + "offenceResults": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterResult.json" + } + } + }, + "required": ["offenceCode", "orderIndex", "offenceTitle"], + "additionalProperties": false +} +``` diff --git a/specs/001-informant-register-local-schema/data-model.md b/specs/001-informant-register-local-schema/data-model.md new file mode 100644 index 000000000..186137dce --- /dev/null +++ b/specs/001-informant-register-local-schema/data-model.md @@ -0,0 +1,196 @@ +# Data Model: Include Verdict in SJP Results + +**Date**: 2026-06-16 | **Branch**: `CIMD-3915-informant-register-local-schema` + +## New Local Types (`uk.gov.moj.cpp.results.domain.informant.model`) + +All types are hand-written POJOs with explicit builder, constructors, and getters. No Lombok. No Jackson annotations beyond `@JsonCreator` / `@JsonProperty` where needed for deserialization. + +### `Verdict` +``` +verdictCode : String (optional — "G", "N", "PSJ") +verdictDate : String (optional — yyyy-MM-dd; co-present with verdictCode) +verdictType : String (optional — "FOUND_GUILTY", "FOUND_NOT_GUILTY", "PROVED_SJP") +``` +Constraints: `verdictCode` and `verdictDate` are co-dependent — both present or both absent. + +### `InformantRegisterOffence` (replaces core-domain) +``` +offenceCode : String (required) +offenceTitle : String (required) +orderIndex : Integer (required) +originatingCaseUrn : String (optional) +pleaValue : String (optional) +verdict : Verdict (optional — replaces flat verdictCode) +offenceResults : List (optional, min 1 if present) +``` + +### `InformantRegisterResult` (replaces core-domain) +``` +resultText : String (required) +cjsResultCode : String (optional) +resultData : InformantRegisterResultData (optional) +``` + +### `InformantRegisterResultData` (replaces core-domain) +``` +amount : String (optional) +nextHearingDate : String (optional — date-time) +nextCourtLocation : String (optional) +durationValue : String (optional) +durationUnit : String (optional) +durationStartDate : String (optional — date-time) +durationEndDate : String (optional — date-time) +secondaryDurationValue : String (optional) +secondaryDurationUnit : String (optional) +``` + +### `InformantRegisterCaseOrApplication` (replaces core-domain) +``` +caseOrApplicationReference : String (required) +arrestSummonsNumber : String (optional) +applicationParticulars : String (optional) +offences : List (required, min 1) +results : List (optional, min 1 if present) +``` + +### `InformantRegisterDefendant` (replaces core-domain) +``` +name : String (required) +address1 : String (required) +address2–5 : String (optional) +postCode : String (optional) +dateOfBirth : String (optional — date) +nationality : String (optional) +title : String (optional) +firstName : String (optional) +lastName : String (optional) +prosecutionCasesOrApplications : List (optional, min 1 if present) +results : List (optional, min 1 if present) +``` + +### `InformantRegisterHearing` (replaces core-domain) +``` +courtRoom : String (required) +hearingStartTime : String (required — time) +defendants : List (required, min 1) +``` + +### `InformantRegisterHearingVenue` (replaces core-domain) +``` +ljaName : String (optional) +courtHouse : String (required) +courtSessions : List (required, min 1) +``` + +### `InformantRegisterRecipient` (replaces core-domain) +``` +recipientName : String (required) +emailAddress1 : String (required) +emailAddress2 : String (optional) +emailTemplateName : String (required) +``` + +### `InformantRegisterDocumentRequest` (replaces core-domain) +``` +registerDate : ZonedDateTime (required) +hearingDate : ZonedDateTime (required) +hearingId : UUID (required) +prosecutionAuthorityId : UUID (required) +prosecutionAuthorityCode : String (required) +prosecutionAuthorityOuCode : String (optional) +majorCreditorCode : String (optional) +prosecutionAuthorityName : String (optional) +fileName : String (required) +recipients : List (optional, min 1 if present) +hearingVenue : InformantRegisterHearingVenue (required) +groupId : UUID (optional) +``` + +### `ProsecutorResult` (replaces core-domain) +``` +startDate : LocalDate (required) +endDate : LocalDate (optional) +prosecutionAuthorityId : UUID (required) +prosecutionAuthorityCode : String (required) +prosecutionAuthorityName : String (optional) +prosecutionAuthorityOuCode : String (optional) +majorCreditorCode : String (optional) +hearingVenues : List (optional) +``` + +--- + +## New Domain Event Types + +### `InformantRegisterRecordedV2` (new — in results-domain-common or domain-event) +``` +prosecutionAuthorityId : UUID (required) +informantRegister : InformantRegisterDocumentRequest (local, required) +``` +Event name: `results.event.informant-register-recorded-v2` + +### `InformantRegisterGeneratedV2` (new) +``` +informantRegisterDocumentRequests : List (local, required) +systemGenerated : Boolean (optional) +``` +Event name: `results.event.informant-register-generated-v2` + +--- + +## JSON Schema Files (new / modified) + +### New local sub-schemas under `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/` + +| File | `$id` namespace | +|------|-----------------| +| `verdict.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/verdict.json` | +| `informantRegisterOffence.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterOffence.json` | +| `informantRegisterResult.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterResult.json` | +| `informantRegisterResultData.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterResultData.json` | +| `informantRegisterCaseOrApplication.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterCaseOrApplication.json` | +| `informantRegisterDefendant.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDefendant.json` | +| `informantRegisterHearing.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterHearing.json` | +| `informantRegisterHearingVenue.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterHearingVenue.json` | +| `informantRegisterRecipient.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterRecipient.json` | +| `informantRegisterDocumentRequest.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json` | +| `prosecutorResult.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/prosecutorResult.json` | + +### New V2 event schemas under `results-domain/results-domain-common/src/main/resources/json/schema/` + +| File | `$id` namespace | +|------|-----------------| +| `results.event.informant-register-recorded-v2.json` | `http://justice.gov.uk/results/courts/informant-register-recorded-v2.json` | +| `results.event.informant-register-generated-v2.json` | `http://justice.gov.uk/results/courts/informant-register-generated-v2.json` | + +### Modified command schema under `results-command/results-command-api/src/raml/json/schema/` + +| File | Change | +|------|--------| +| `results.add-informant-register.json` | Replace `$ref` to core-domain with local `$ref` to `informantRegisterDocumentRequest.json` | + +### Modified query schema under `results-query/results-query-api/src/raml/json/schema/` + +| File | Change | +|------|--------| +| `results.prosecutor-results.json` | Replace `$ref` to core-domain `prosecutorResult.json` with local `$ref` including `verdict` object | + +--- + +## State Transitions (Informant Register lifecycle — unchanged) + +``` +RECORDED → GENERATED → NOTIFIED +``` +The lifecycle is unaffected by this change. The verdict enrichment only alters the payload shape within each state. + +--- + +## Backward Compatibility + +Pre-migration events in the event store carry `informantRegister.hearingVenue.courtSessions[*].defendants[*].prosecutionCasesOrApplications[*].offences[*].verdictCode` as a flat string. When these are replayed: + +- V1 listener handlers (`informant-register-recorded`, `informant-register-generated`) still deserialize correctly using the core-domain types — these handlers are not removed. +- The local `InformantRegisterOffence` POJO must tolerate a missing `verdict` field when deserialized from stored JSON — `verdict` is optional. +- The `InformantRegisterEntity.payload` (stored as raw JSON string) for pre-migration records will not have a `verdict` object — the query view returns those offences without a `verdict` field (correct per FR-003). diff --git a/specs/001-informant-register-local-schema/plan.md b/specs/001-informant-register-local-schema/plan.md new file mode 100644 index 000000000..e64d588c2 --- /dev/null +++ b/specs/001-informant-register-local-schema/plan.md @@ -0,0 +1,299 @@ +# Implementation Plan: Include Verdict in SJP Results + +**Branch**: `CIMD-3915-informant-register-local-schema` | **Date**: 2026-06-16 +**Spec**: [spec.md](spec.md) | **Research**: [research.md](research.md) | **Data model**: [data-model.md](data-model.md) | **Contracts**: [contracts/event-contracts.md](contracts/event-contracts.md) + +## Summary + +Decouple the informant-register command and event schemas from the shared core-domain by introducing local JSON schemas and hand-written POJO types. Replace the flat `verdictCode` string on the offence schema with a structured `verdict` object (`verdictCode`, `verdictDate`, `verdictType`). Introduce two new V2 domain events (`informant-register-recorded-v2`, `informant-register-generated-v2`) that carry the local schema. Update the `results.prosecutor-results` query response to include the structured verdict. All changes follow strict TDD (red→green→refactor) with contracts updated before Java (Constitution Principle I), and three-layer discipline across command / listener / processor (Principle II). + +--- + +## Technical Context + +**Language/Version**: Java 17 (`centos8-j17` CI demand; local `mvn17` alias) +**Primary Dependencies**: CPP `service-parent-pom:17.103.9`; JEE/CDI; JUnit 5 + Mockito; Jackson (ObjectMapper via framework producer) +**Storage**: `DS.eventstore` (event store); `DS.results` (viewstore — `InformantRegisterEntity` table, payload stored as JSON string) +**Testing**: JUnit 5 (`@ExtendWith(MockitoExtension.class)`); `mvn test` for unit; `./runIntegrationTests.sh` for ITs +**Target Platform**: WildFly WAR on Docker (Linux / `centos8-j17`) +**Project Type**: Event-sourced bounded context (CQRS) +**Performance Goals**: No new performance requirements — existing throughput targets unchanged +**Constraints**: No Lombok; no Spring; no wildcard imports; no `System.out`; hand-written POJOs only; contracts before Java + +--- + +## Constitution Check + +| Principle | Status | Notes | +|-----------|--------|-------| +| **I. RAML / JSON-Schema Contract First** | PASS | All schemas and subscription descriptors updated before any Java change (see implementation phases below) | +| **II. CQRS Three-Layer Discipline** | PASS | Command side + listener + processor all addressed; see three-layer impact table in research.md | +| **III. CPP Framework Idioms** | PASS | `@ServiceComponent`, `@Handles`, `Aggregate.apply`, hand-written POJOs — no Spring/Lombok/JMS/JDBC | +| **IV. Spec-Driven Build Loop** | PASS | Flowing through speckit; code-reviewer + qa + spec-validator agents will run after implementation | +| **V. HMCTS CPP Standards** | PASS | Java 17; Maven; WAR/WildFly; JUnit+Mockito; no wildcard imports | +| **VI. Schema-Subscription Symmetry** | PASS | Both new V2 events have matching schemas AND subscription entries before Java; namespace `http://justice.gov.uk/results/courts/...` confirmed | +| **VII. No System.out / System.err** | PASS | SLF4J only; enforced in all new production and test code | +| **VIII. TDD** | PASS | Explicit TDD sequence in every phase: failing test first, then production code | + +--- + +## Project Structure + +### Documentation (this feature) + +```text +specs/001-informant-register-local-schema/ +├── spec.md +├── plan.md ← this file +├── research.md +├── data-model.md +├── contracts/ +│ └── event-contracts.md +├── checklists/ +│ └── requirements.md +└── tasks.md ← generated by /speckit-tasks +``` + +### Source Code — affected modules + +```text +results-domain/results-domain-common/ +├── src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ +│ ├── Verdict.java [NEW] +│ ├── InformantRegisterOffence.java [NEW — replaces core-domain] +│ ├── InformantRegisterResult.java [NEW] +│ ├── InformantRegisterResultData.java [NEW] +│ ├── InformantRegisterCaseOrApplication.java [NEW] +│ ├── InformantRegisterDefendant.java [NEW] +│ ├── InformantRegisterHearing.java [NEW] +│ ├── InformantRegisterHearingVenue.java [NEW] +│ ├── InformantRegisterRecipient.java [NEW] +│ ├── InformantRegisterDocumentRequest.java [NEW] +│ └── ProsecutorResult.java [NEW] +├── src/main/java/uk/gov/moj/cpp/results/domain/event/ +│ ├── InformantRegisterRecordedV2.java [NEW] +│ └── InformantRegisterGeneratedV2.java [NEW] +└── src/main/resources/json/schema/ + ├── informantRegisterDocument/ [NEW directory] + │ ├── verdict.json + │ ├── informantRegisterOffence.json + │ ├── informantRegisterResult.json + │ ├── informantRegisterResultData.json + │ ├── informantRegisterCaseOrApplication.json + │ ├── informantRegisterDefendant.json + │ ├── informantRegisterHearing.json + │ ├── informantRegisterHearingVenue.json + │ ├── informantRegisterRecipient.json + │ ├── informantRegisterDocumentRequest.json + │ └── prosecutorResult.json + ├── results.event.informant-register-recorded-v2.json [NEW] + └── results.event.informant-register-generated-v2.json [NEW] + +results-command/results-command-api/ +└── src/raml/json/schema/ + └── results.add-informant-register.json [MODIFIED — local $ref] + +results-event-sources/ +└── src/yaml/event-sources.yaml [check — no new topics needed] + +results-event/results-event-listener/ +├── src/yaml/subscriptions-descriptor.yaml [MODIFIED — add V2 entries] +└── src/main/java/uk/gov/moj/cpp/results/event/ + └── InformantRegisterEventListener.java [MODIFIED — add V2 handlers] + +results-event/results-event-processor/ +├── src/yaml/subscriptions-descriptor.yaml [MODIFIED — add V2 entry] +├── src/main/java/uk/gov/moj/cpp/results/event/processor/ +│ └── InformantRegisterEventProcessor.java [MODIFIED — add V2 handler] +└── src/main/java/uk/gov/moj/cpp/results/event/processor/model/ + └── InformantRegisterDocument.java [MODIFIED — verdict extraction] + +results-domain/results-domain-aggregate/ +└── src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ + └── ProsecutionAuthorityAggregate.java [MODIFIED — local types] + +results-command/results-command-handler/ +└── src/main/java/uk/gov/moj/cpp/results/command/handler/ + └── InformantRegisterHandler.java [MODIFIED — emit V2 events] + +results-query/results-query-api/ +└── src/raml/json/schema/ + └── results.prosecutor-results.json [MODIFIED — local $ref] + +results-query/results-query-view/ +└── src/main/java/uk/gov/moj/cpp/results/query/view/ + └── ProsecutorResultsQueryView.java [MODIFIED — local types] +``` + +--- + +## Implementation Phases + +> **TDD Rule (Principle VIII)**: In every phase below, the failing test is written and confirmed to fail for the right reason (assertion failure, not compilation error) BEFORE the production code is written. + +--- + +### Phase 0 — Contracts and Schemas First (Principle I) + +No Java. No tests. Pure contract artefacts. + +**0.1** Create `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/` directory and write all 11 local sub-schemas (see data-model.md for full list). Start with `verdict.json` as it has no dependencies, then build up in dependency order. + +**0.2** Write `results.event.informant-register-recorded-v2.json` and `results.event.informant-register-generated-v2.json` under `results-domain-common/src/main/resources/json/schema/` referencing the local sub-schemas. + +**0.3** Update `results.add-informant-register.json` (command-api) to replace the core-domain `$ref` with the local `informantRegisterDocumentRequest.json` reference. + +**0.4** Write local `prosecutorResult.json` sub-schema and update `results.prosecutor-results.json` (query-api) to reference it. + +**0.5** Add V2 subscription entries to `results-event-listener/src/yaml/subscriptions-descriptor.yaml` (both `recorded-v2` and `generated-v2`). + +**0.6** Add V2 subscription entry to `results-event-processor/src/yaml/subscriptions-descriptor.yaml` (`generated-v2` only). + +**Gate**: `mvn clean install -DskipTests` on affected modules must succeed before proceeding to Phase 1. + +--- + +### Phase 1 — Local POJO Types (TDD) + +**Order**: write the deepest types first (no dependencies), build up. + +**1.1 `Verdict` POJO** +- Write failing test in `results-domain-common`: `VerdictTest` — assert builder creates instance with all three fields, assert null fields are tolerated. +- Confirm test fails (compilation — class missing). Write `Verdict.java`. Test goes green. + +**1.2 `InformantRegisterOffence` POJO** +- Write failing test: `InformantRegisterOffenceTest` — assert builder sets `verdict`, assert `verdictCode` field is absent (compilation forces migration). +- Write `InformantRegisterOffence.java`. Test goes green. + +**1.3 Remaining POJOs** (`InformantRegisterResult`, `InformantRegisterResultData`, `InformantRegisterCaseOrApplication`, `InformantRegisterDefendant`, `InformantRegisterHearing`, `InformantRegisterHearingVenue`, `InformantRegisterRecipient`, `InformantRegisterDocumentRequest`, `ProsecutorResult`) +- Each gets a minimal builder/getter test before the production POJO is written. + +**1.4 V2 event POJOs** (`InformantRegisterRecordedV2`, `InformantRegisterGeneratedV2`) +- Write failing tests asserting correct event name constants and builder fields. +- Write production POJOs. + +**Gate**: `mvn -pl results-domain/results-domain-common test` green. + +--- + +### Phase 2 — Command Handler (TDD) + +**2.1 Gap-fill existing tests** — Review `InformantRegisterHandlerTest`. Add any missing scenarios (e.g., no test for `handleAddInformantRegisterToEventStream` emitting event with offence verdict data). Add failing tests first. + +**2.2 V2 emit test** — Add failing test to `InformantRegisterHandlerTest`: +- `handleAddInformantRegisterToEventStream` with offence carrying a `verdict` object → assert emitted event name is `results.event.informant-register-recorded-v2` and payload carries `verdict.verdictCode`. +- Confirm test fails (V2 event not yet emitted). + +**2.3** Update `InformantRegisterHandler.handleAddInformantRegisterToEventStream` to accept local `InformantRegisterDocumentRequest` and emit `InformantRegisterRecordedV2`. Keep V1 emit for backward compat (dual-emit or replace — confirm with team; default to V2-only if this is a greenfield migration). + +**2.4 Generate V2 test** — Add failing test: `processRequests` emits `results.event.informant-register-generated-v2`. + +**2.5** Update `processRequests` to emit `InformantRegisterGeneratedV2`. + +**Gate**: `mvn -pl results-command/results-command-handler test` green. + +--- + +### Phase 3 — Aggregate (TDD) + +**3.1 Gap-fill** — Review `ProsecutionAuthorityAggregateTest`. Add missing scenarios for `apply(InformantRegisterGeneratedV2)`. + +**3.2 V2 apply test** — Add failing test: `ProsecutionAuthorityAggregateTest` — `apply(InformantRegisterGeneratedV2)` sets `informantRegisterRecipients` from local `InformantRegisterDocumentRequest`. + +**3.3** Update `ProsecutionAuthorityAggregate.apply` to handle `InformantRegisterGeneratedV2` using local types. + +**Gate**: `mvn -pl results-domain/results-domain-aggregate test` green. + +--- + +### Phase 4 — Event Listener (TDD) + +**4.1 Gap-fill** — Review `InformantRegisterEventListenerTest`. Add missing scenarios where applicable. + +**4.2 V2 recorded handler test** — Add failing test: `saveInformantRegisterV2` handles `results.event.informant-register-recorded-v2` envelope, deserializes local `InformantRegisterDocumentRequest`, saves entity with same fields as V1. + +**4.3** Add `saveInformantRegisterV2` handler to `InformantRegisterEventListener` with `@Handles("results.event.informant-register-recorded-v2")`. + +**4.4 V2 generated handler test** — Add failing test: `generateInformantRegisterV2` handles `results.event.informant-register-generated-v2`, updates entity status to GENERATED. + +**4.5** Add `generateInformantRegisterV2` handler. + +**Gate**: `mvn -pl results-event/results-event-listener test` green. + +--- + +### Phase 5 — Event Processor (TDD) + +**5.1 Gap-fill** — Review `InformantRegisterEventProcessorTest`. Identify missing coverage for `buildOffenceDetails` (currently no assertion on `verdictCode` in the CSV output). + +**5.2 Verdict extraction test** — Add failing test: `generateInformantRegister` with offence carrying `verdict.verdictCode: "G"` → assert CSV row contains `verdictCode = "G"`. + +**5.3** Update `buildOffenceDetails` in `InformantRegisterEventProcessor`: replace `offence.getVerdictCode()` with null-safe `offence.getVerdict() != null ? offence.getVerdict().getVerdictCode() : null`. + +**5.4 V2 handler test** — Add failing test: `generateInformantRegisterV2` handles `results.event.informant-register-generated-v2` envelope and produces the same CSV output. + +**5.5** Add `generateInformantRegisterV2` handler to `InformantRegisterEventProcessor` with `@Handles("results.event.informant-register-generated-v2")`. + +**Gate**: `mvn -pl results-event/results-event-processor test` green. + +--- + +### Phase 6 — Query View (TDD) + +**6.1 Gap-fill** — Review `ProsecutorResultsQueryViewTest`. Identify missing scenario: no test asserting verdict data in response. + +**6.2 Verdict in response test** — Add failing test to `ProsecutorResultsQueryViewTest`: +- Store `InformantRegisterEntity` with a payload JSON that includes `verdict.verdictCode: "G"` on an offence. +- Call `getProsecutorResults`. +- Assert response JSON contains `hearingVenues[0].courtSessions[0].defendants[0].prosecutionCasesOrApplications[0].offences[0].verdict.verdictCode = "G"`. +- Confirm test fails (current code uses core-domain `InformantRegisterDocumentRequest` which has no `verdict` field). + +**6.3** Update `ProsecutorResultsQueryView` to use local `InformantRegisterDocumentRequest` and local `ProsecutorResult`. + +**6.4 Absent verdict test** — Add failing test: offence with no `verdict` in stored JSON → response offence has no `verdict` field (not null, absent). + +**6.5 NO_VERDICT omit test** — Add failing test: stored offence with `"verdict": null` → response omits `verdict` field. + +**Gate**: `mvn -pl results-query/results-query-view test` green. + +--- + +### Phase 7 — Backward Compatibility (TDD) + +**7.1 Pre-migration recorded replay test** — Add failing test to `InformantRegisterEventListenerTest`: +- Build a V1 `informant-register-recorded` JSON envelope with a flat `verdictCode` string field on an offence (old schema). +- Send to the V1 `saveInformantRegister` handler. +- Assert: no exception thrown; entity saved with correct `prosecutionAuthorityId` and `registerDate`. + +**7.2** Confirm this test passes without additional production code changes (V1 handler still uses core-domain types — old schema still valid). + +**7.3 Pre-migration generated replay test** — Same pattern for `informant-register-generated` V1 with flat `verdictCode`. + +**Gate**: `mvn test` on all affected modules green. + +--- + +### Phase 8 — Full Build Verification + +```bash +mvn clean install +``` + +All unit tests green. Zero compilation errors. Zero schema-subscription drift. + +--- + +## Complexity Tracking + +No constitution violations. All principles satisfied. + +--- + +## Risks + +| Risk | Likelihood | Mitigation | +|------|-----------|------------| +| Jackson deserialization of local POJOs from stored JSON fails silently | Medium | Explicit unit tests for deserialization of payload JSON strings to local types (Phase 6.2) | +| Namespace mismatch between schema `$id` and subscription `schema_uri` | Low | Phase 0 contract review; spec-validator agent run after implementation | +| Missing `@JsonCreator` / `@JsonProperty` on local POJO constructors | Medium | Phase 1 POJO tests include deserialization roundtrip assertions | +| Dual-emit in handler causes out-of-order replay issues | Low | V1 and V2 events are independent; listeners handle each separately | diff --git a/specs/001-informant-register-local-schema/quickstart.md b/specs/001-informant-register-local-schema/quickstart.md new file mode 100644 index 000000000..a1a3c9122 --- /dev/null +++ b/specs/001-informant-register-local-schema/quickstart.md @@ -0,0 +1,71 @@ +# Quickstart: CIMD-3915 — Include Verdict in SJP Results + +**Branch**: `CIMD-3915-informant-register-local-schema` +**Plan**: [plan.md](plan.md) | **Spec**: [spec.md](spec.md) + +## What this change does + +Replaces the core-domain dependency for informant-register schemas with locally-owned schemas and POJOs. Adds a structured `verdict` object (`verdictCode`, `verdictDate`, `verdictType`) to offences, replacing the flat `verdictCode` string. Exposes verdict data through the `results.prosecutor-results` query API. + +## Key commands + +```bash +# Run unit tests on a single module +mvn -pl results-domain/results-domain-common test +mvn -pl results-domain/results-domain-aggregate test +mvn -pl results-command/results-command-handler test +mvn -pl results-event/results-event-listener test +mvn -pl results-event/results-event-processor test +mvn -pl results-query/results-query-view test + +# Full build with unit tests +mvn clean install + +# Full build, skip tests (for iterating on compile errors) +mvn clean install -DskipTests +``` + +## Implementation order (TDD) + +Follow the phases in [plan.md](plan.md) strictly: + +1. **Phase 0** — Write all JSON schemas and update subscription descriptors first (no Java) +2. **Phase 1** — Write local POJO tests, then POJOs (deepest types first: `Verdict` → `InformantRegisterOffence` → …) +3. **Phase 2** — Command handler: gap-fill tests, then V2 emit tests, then production handler changes +4. **Phase 3** — Aggregate: gap-fill tests, then V2 apply test, then production aggregate change +5. **Phase 4** — Event listener: gap-fill tests, then V2 handler tests, then production handlers +6. **Phase 5** — Event processor: gap-fill tests, then verdict extraction test, then V2 handler test, then production changes +7. **Phase 6** — Query view: gap-fill tests, then verdict-in-response test, then production view change +8. **Phase 7** — Backward compat tests (V1 replay) +9. **Phase 8** — `mvn clean install` (full build gate) + +## New local packages + +| Package | Purpose | +|---------|---------| +| `uk.gov.moj.cpp.results.domain.informant.model` | 11 hand-written POJO types (in `results-domain-common`) | +| `uk.gov.moj.cpp.results.domain.event` | `InformantRegisterRecordedV2`, `InformantRegisterGeneratedV2` | + +## New JSON schemas + +All under `results-domain/results-domain-common/src/main/resources/json/schema/`: + +- `informantRegisterDocument/verdict.json` — new +- `informantRegisterDocument/informantRegisterOffence.json` — local copy with `verdict` object +- 9 other sub-schemas — local copies of core-domain sub-schemas +- `results.event.informant-register-recorded-v2.json` — new V2 event schema +- `results.event.informant-register-generated-v2.json` — new V2 event schema + +## What is NOT changing + +- V1 event handlers (`informant-register-recorded`, `informant-register-generated`) — kept for backward-compat replay +- Viewstore schema — no DDL change; verdict lives in the existing `payload` JSON column +- `informant-register-notified` / `informant-register-notified-v2` — out of scope (carry no offence data) +- CSV format — `verdictCode` string is still present; now sourced from `verdict.verdictCode` + +## Constitution gates (must not skip) + +- Contracts before Java (Principle I) — Phase 0 must be complete before Phase 1 +- Three-layer discipline (Principle II) — all three layers must be tested and updated +- TDD (Principle VIII) — failing test before every production code change +- Schema-subscription symmetry (Principle VI) — every new event has both a schema file AND a subscription entry diff --git a/specs/001-informant-register-local-schema/research.md b/specs/001-informant-register-local-schema/research.md new file mode 100644 index 000000000..7385c8d17 --- /dev/null +++ b/specs/001-informant-register-local-schema/research.md @@ -0,0 +1,107 @@ +# Research: Include Verdict in SJP Results — results context + +**Date**: 2026-06-16 | **Branch**: `CIMD-3915-informant-register-local-schema` + +## Decision 1: Event Versioning Strategy + +**Decision**: Introduce new V2 domain events (`results.event.informant-register-recorded-v2`, `results.event.informant-register-generated-v2`) that carry the new local schema, while keeping existing V1 event handlers for backward-compatible replay of historical events. + +**Rationale**: The existing event store contains pre-migration events carrying the old schema (core-domain references, flat `verdictCode`). These must remain replayable without errors (FR-012, SC-005). The project already uses this pattern — `InformantRegisterNotifiedV2` was introduced alongside `InformantRegisterNotified` — confirming it is the team's established approach. + +**Alternatives considered**: +- In-place schema mutation (no versioning) — rejected: would break replay of historical events already in the event store. +- Migration job to rewrite historic events — rejected: out of scope; event sourcing requires immutable event history. + +--- + +## Decision 2: Local POJO Strategy + +**Decision**: Hand-write local POJO types in `results-domain/results-domain-common` under the package `uk.gov.moj.cpp.results.domain.informant.model` to replace the core-domain generated classes used in the informant-register flows. Each POJO uses explicit constructors, getters, and a nested `Builder` class (no Lombok, no Jackson codegen). + +**Rationale**: The core-domain generated classes are produced by a codegen step that cannot be controlled from this context. Owning local POJOs is the only way to evolve the schema (e.g., replace `verdictCode` with `verdict` object) independently. The `results-domain-common` module already hosts shared domain helpers (`InformantRegisterHelper`) making it the correct home. + +**Alternatives considered**: +- Keep using core-domain types and add a `verdict` enrichment adapter — rejected: still requires core-domain codegen to expose the new field, defeating the decoupling goal. +- Generate POJOs from local JSON schemas using the CPP codegen plugin — rejected: adds build complexity; constitution Principle III confirms hand-written POJOs are standard where codegen is not already wired. + +**Types needed** (all in `uk.gov.moj.cpp.results.domain.informant.model`): + +| Local type | Replaces core-domain type | Key change | +|------------|--------------------------|------------| +| `InformantRegisterDocumentRequest` | `uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest` | Local refs | +| `InformantRegisterHearingVenue` | `...InformantRegisterHearingVenue` | Local refs | +| `InformantRegisterHearing` | `...InformantRegisterHearing` | Local refs | +| `InformantRegisterDefendant` | `...InformantRegisterDefendant` | Local refs | +| `InformantRegisterCaseOrApplication` | `...InformantRegisterCaseOrApplication` | Local refs | +| `InformantRegisterOffence` | `...InformantRegisterOffence` | `verdict: Verdict` replaces `verdictCode: String` | +| `Verdict` | *(new)* | `verdictCode`, `verdictDate`, `verdictType` | +| `InformantRegisterResult` | `...InformantRegisterResult` | Local refs | +| `InformantRegisterResultData` | `...InformantRegisterResultData` | No change | +| `InformantRegisterRecipient` | `...InformantRegisterRecipient` | No change | +| `ProsecutorResult` | `...ProsecutorResult` | Uses local `InformantRegisterHearingVenue` | + +--- + +## Decision 3: Local JSON Schema Layout + +**Decision**: Place all local informant-register sub-schemas under `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/` with namespace `http://justice.gov.uk/results/courts/informantRegisterDocument/...` (matching the sibling V2 event namespace pattern). Command schema and query schema keep their existing file paths but replace the `$ref` with a local namespace ref. + +**Rationale**: The existing V2 event schemas use the `http://justice.gov.uk/results/courts/...` namespace (confirmed from `informant-register-notified-v2.json`). New local sub-schemas should use `http://justice.gov.uk/results/courts/informantRegisterDocument/...` for consistency. + +**Namespace mapping**: + +| Schema file | Old `$id` / `$ref` | New `$id` | +|-------------|---------------------|-----------| +| `informantRegisterOffence.json` (local) | `http://justice.gov.uk/core/courts/informantRegisterDocument/informantRegisterOffence.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterOffence.json` | +| `informantRegisterDocumentRequest.json` (local) | `http://justice.gov.uk/core/courts/informantRegisterDocument/informantRegisterDocumentRequest.json` | `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json` | +| `informant-register-recorded-v2.json` | *(new)* | `http://justice.gov.uk/results/courts/informant-register-recorded-v2.json` | +| `informant-register-generated-v2.json` | *(new)* | `http://justice.gov.uk/results/courts/informant-register-generated-v2.json` | +| `results.add-informant-register.json` (command) | `$ref` to core-domain | Self-contained with local refs | +| `results.prosecutor-results.json` (query) | `$ref` to core-domain `prosecutorResult.json` | Local inline with `verdict` object | + +--- + +## Decision 4: Command Handler Emit Strategy + +**Decision**: `handleAddInformantRegisterToEventStream` will emit the existing `InformantRegisterRecorded` event (V1, kept for backward compat with any downstream that still replays V1 events) AND a new `InformantRegisterRecordedV2` event carrying the local type. The listener adds a V2 handler alongside the existing V1 handler. + +**Rationale**: Dual-emit ensures both old replays (via V1) and new downstream (via V2) work correctly. The `processRequests` method similarly emits `InformantRegisterGeneratedV2` for the generate commands. + +**Alternatives considered**: +- Emit only V2 — rejected: breaks replay of the listener against historical V1 events (listener subscribed to V1 would miss new events; V2-only listener would miss historical events). +- Emit only V1 with an updated payload schema — rejected: backward-incompatible schema change on an existing event is a runtime risk. + +--- + +## Decision 5: ProsecutorResultsQueryView Migration + +**Decision**: `ProsecutorResultsQueryView` will be updated to use local `InformantRegisterDocumentRequest` and local `ProsecutorResult` types. The payload stored in `InformantRegisterEntity.payload` (as a JSON string) will be deserialized to the local `InformantRegisterDocumentRequest`, which includes the `verdict` object on offences. No viewstore schema change is required. + +**Rationale**: The payload is stored as a raw JSON string in the `InformantRegisterEntity`. As long as newly ingested records contain the `verdict` object in the JSON, the query view will naturally return it. Pre-migration records (without `verdict`) will simply have no `verdict` field in the response — consistent with FR-003 (omit when absent). + +**Alternatives considered**: +- Add a separate `verdictCode` column to the viewstore entity — rejected: the payload is stored as a JSON blob; extracting a field to a column adds Liquibase migration work and is unnecessary for read-only query purposes. + +--- + +## Decision 6: InformantRegisterDocument CSV Model + +**Decision**: The CSV `InformantRegisterDocument` model's `verdictCode` field is retained but now populated from `offence.getVerdict().getVerdictCode()` (null-safe, returning empty string if no verdict). The CSV format does not change. + +**Rationale**: The CSV format is consumed by downstream file recipients and is out of scope for the verdict object change. The CSV only needs the `verdictCode` string, which is still available as `verdict.verdictCode`. + +--- + +## Scope Confirmation — Three-Layer Impact + +| Layer | Events affected | Action | +|-------|----------------|--------| +| Command side | `informant-register-recorded-v2` (new), `informant-register-generated-v2` (new) | Handler emits V2; aggregate uses local types | +| Event listener | `informant-register-recorded-v2` (new), `informant-register-generated-v2` (new); V1 handlers kept | Add V2 handler methods; keep V1 for backward compat | +| Event processor | `informant-register-generated-v2` (new); existing `informant-register-generated` V1 kept for CSV | Add V2 handler; update `buildOffenceDetails` to read `verdict` object | +| Query | `results.prosecutor-results` | Schema + view updated to include `verdict` object | + +**Not affected** (explicitly out of scope): +- `informant-register-notified`, `informant-register-notified-v2`, `informant-register-notification-ignored` — carry no offence data +- Financial results aggregates, results aggregate, defendant aggregate +- All non-informant-register event flows diff --git a/specs/001-informant-register-local-schema/spec.md b/specs/001-informant-register-local-schema/spec.md new file mode 100644 index 000000000..ef4d1693d --- /dev/null +++ b/specs/001-informant-register-local-schema/spec.md @@ -0,0 +1,155 @@ +# Feature Specification: Include Verdict in SJP Results — results context changes + +**Feature Branch**: `CIMD-3915-informant-register-local-schema` +**Created**: 2026-06-16 +**Status**: Draft +**Jira**: CIMD-3915 +**Sprint**: 62 +**Scope**: `cpp-context-results` only + +## Overview + +The results context must include structured verdict data in the `results.prosecutor-results` query response so that downstream consumers can accurately report the legal outcome for each SJP offence. Two enabling changes are required: + +1. Decouple the informant register offence schema from the shared core-domain — own it locally so it can evolve independently. +2. Replace the flat `verdictCode` string on the offence with a structured `verdict` object (`verdictCode`, `verdictDate`, `verdictType`). + +The delivery surface is the `results.prosecutor-results` query. The command side (`add-informant-register`) is the ingest path that must accept and store the enriched offence data. + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - Add Informant Register Accepts and Stores Enriched Verdict Data (Priority: P1) + +The `results.add-informant-register` command accepts an informant register document request containing offences with the structured `verdict` object and records it using a locally-owned schema. + +**Why this priority**: This is the ingest entry point. Downstream generation and query flows depend on correctly stored data. + +**Independent Test**: Submit `results.add-informant-register` with an offence carrying a `verdict` object; confirm the recorded domain event stores all three verdict fields. Confirm a request with the old flat `verdictCode` field fails validation against the new schema. + +**Acceptance Scenarios**: + +1. **Given** a valid `add-informant-register` command where an offence carries a `verdict` object with `verdictCode`, `verdictDate`, and `verdictType`, **When** the command is received, **Then** the domain event is stored with all three verdict fields present on the offence. + +2. **Given** a valid `add-informant-register` command where an offence carries no `verdict` object, **When** the command is received, **Then** the event is stored without verdict data (verdict is optional on an offence). + +3. **Given** an `add-informant-register` command where an offence carries a legacy flat `verdictCode` string (old schema), **When** the command is received, **Then** the request is rejected with a validation error. + +--- + +### User Story 2 - Generate Informant Register Produces Enriched Generation Event (Priority: P2) + +The `results.generate-informant-register` and `results.generate-informant-register-by-date` commands produce a generation event that carries informant register document requests using the locally-owned schema, including the structured verdict object on offences. + +**Why this priority**: Generation reads from stored records and publishes events consumed downstream; it must propagate the enriched data correctly. + +**Independent Test**: Trigger `results.generate-informant-register` against stored records with verdict data; confirm the generated domain event contains offences with the `verdict` object intact. + +**Acceptance Scenarios**: + +1. **Given** stored informant register records include offences with a `verdict` object, **When** `results.generate-informant-register` is triggered, **Then** the `informant-register-generated` domain event carries those offences with the structured verdict data. + +2. **Given** stored informant register records include offences with no `verdict` object, **When** `results.generate-informant-register-by-date` is triggered, **Then** the `informant-register-generated` domain event carries those offences without a verdict field. + +3. **Given** no informant register records exist in `RECORDED` status, **When** `results.generate-informant-register` is triggered, **Then** no events are emitted and the command completes silently. + +--- + +### User Story 3 - Prosecutor Results Query Returns Structured Verdict on Offences (Priority: P1) + +The `results.prosecutor-results` query response includes a `verdict` object on each offence where a verdict was recorded, using the verdict type mapping from CIMD-3915. + +**Why this priority**: This is the primary delivery surface for this ticket within the results context. + +**Independent Test**: Query `results.prosecutor-results` for a prosecution authority with stored offences covering FOUND_GUILTY, FOUND_NOT_GUILTY, PROVED_SJP, NO_VERDICT, and null verdict; confirm each offence is returned with the correct verdict shape per the mapping table. + +**Acceptance Scenarios**: + +1. **Given** an offence has verdict FOUND_GUILTY, **When** `results.prosecutor-results` is queried, **Then** the offence includes `verdict` with `verdictCode: "G"` (mandatory), `verdictDate` in `yyyy-MM-dd` (mandatory when verdictCode present, omitted if conviction date is null), and optionally `verdictType: "FOUND_GUILTY"`. + +2. **Given** an offence has verdict FOUND_NOT_GUILTY (decision type DISMISS), **When** `results.prosecutor-results` is queried, **Then** the offence includes `verdict` with `verdictCode: "N"`, `verdictDate`, and optionally `verdictType: "FOUND_NOT_GUILTY"`. + +3. **Given** an offence has verdict PROVED_SJP (proved in absence, no plea), **When** `results.prosecutor-results` is queried, **Then** the offence includes `verdict` with `verdictCode: "PSJ"`, `verdictDate`, and optionally `verdictType: "PROVED_SJP"`. + +4. **Given** an offence has verdict NO_VERDICT or null verdict, **When** `results.prosecutor-results` is queried, **Then** the offence does NOT contain a `verdict` field (omitted entirely — not null). + +5. **Given** a case has multiple offences where some have verdicts and some do not, **When** `results.prosecutor-results` is queried, **Then** each offence is assessed independently — no verdict is mixed or defaulted from one offence to another. + +--- + +### User Story 4 - Pre-migration Events Remain Replayable (Priority: P2) + +Existing domain events stored before this change (carrying the old flat `verdictCode` string) can still be replayed against the aggregate and processed by the event listener without causing runtime errors. + +**Why this priority**: Event sourcing requires backward-compatible replay. Failure here silently breaks historical reconstitution. + +**Independent Test**: Replay a pre-migration `informant-register-recorded` event (with flat `verdictCode`) through the aggregate and event listener; confirm no exception is thrown and the state is consistent. + +**Acceptance Scenarios**: + +1. **Given** a pre-migration `informant-register-recorded` event exists in the event store with a flat `verdictCode` field on an offence, **When** the event is replayed, **Then** no runtime exception is thrown and the aggregate state is consistent. + +2. **Given** a pre-migration `informant-register-generated` event exists with flat `verdictCode` offences, **When** the event is replayed by the event listener, **Then** no runtime exception is thrown. + +--- + +### Edge Cases + +- `verdictCode` and `verdictDate` are co-dependent: if one is present the other must be present; if one is absent both are absent. +- `verdictType` is optional — it is populated from reference data; if the lookup returns no match, `verdictType` is omitted but the rest of the verdict object (`verdictCode`, `verdictDate`) is still returned. +- An offence with an entirely absent `verdict` object is valid — verdict is optional on an offence. +- A `verdict` object with none of its fields set is equivalent to no verdict — the field should be omitted rather than serialised as `{}`. + +## Requirements *(mandatory)* + +### Functional Requirements + +- **FR-001**: The `results.add-informant-register` command payload schema MUST be owned locally within the results context with no runtime `$ref` to any core-domain schema. +- **FR-002**: The `results.generate-informant-register` and `results.generate-informant-register-by-date` command handling MUST use the locally-owned informant register document types. +- **FR-003**: The local informant register offence schema MUST replace the flat `verdictCode` string field with an optional `verdict` object containing: `verdictCode` (string), `verdictDate` (string, `yyyy-MM-dd`), and `verdictType` (string) — all fields optional within the object. +- **FR-004**: `verdictCode` and `verdictDate` MUST be co-reported: both present or both absent. +- **FR-005**: New versioned domain events MUST be introduced for the add-informant-register and generate-informant-register flows that carry offences in the new local schema shape, coexisting with existing events in the event store. +- **FR-006**: The `ProsecutionAuthorityAggregate` MUST be updated to use locally-owned informant register document types. +- **FR-007**: The `results.prosecutor-results` query response MUST include the `verdict` object on offences per the verdict type mapping (FR-003), and MUST omit the field entirely (not null) when verdict is NO_VERDICT or null. +- **FR-008**: When verdict reference data is unavailable for an offence, the system MUST omit `verdictType` but still include `verdictCode` and `verdictDate` where available; the offence MUST still be returned and a warning logged. +- **FR-009**: Each offence MUST be assessed independently; verdict from one offence MUST NOT influence another. +- **FR-010**: All new local schemas MUST use the correct results-context namespace matching sibling schemas in the same descriptor. +- **FR-011**: Pre-migration events already stored in the event store MUST remain replayable without runtime errors — verified by unit test. + +### Key Entities + +- **InformantRegisterOffence (local)**: `offenceCode` (required), `offenceTitle` (required), `orderIndex` (required), `originatingCaseUrn` (optional), `pleaValue` (optional), `verdict` object (optional), `offenceResults` (optional). +- **Verdict**: `verdictCode` (string — "G", "N", "PSJ"), `verdictDate` (string — `yyyy-MM-dd`), `verdictType` (string — "FOUND_GUILTY", "FOUND_NOT_GUILTY", "PROVED_SJP"). All optional; `verdictCode` and `verdictDate` are co-dependent. +- **InformantRegisterDocumentRequest (local)**: Full document — hearing venue, defendants, cases/applications, offences. No external schema references. + +### Verdict Type Mapping + +| Internal verdict type | Verdict code | `verdictType` in payload | Verdict field in payload | +|-----------------------|--------------|--------------------------|--------------------------| +| FOUND_GUILTY | G | FOUND_GUILTY (optional) | Included | +| FOUND_NOT_GUILTY | N | FOUND_NOT_GUILTY (opt.) | Included | +| PROVED_SJP | PSJ | PROVED_SJP (optional) | Included | +| NO_VERDICT | (absent) | (absent) | Omitted entirely | +| null (not set) | (absent) | (absent) | Omitted entirely | + +## Success Criteria *(mandatory)* + +- **SC-001**: All unit tests for the command handler, aggregate, event listener, event processor, and query view pass with no failures. +- **SC-002**: `mvn clean install` on all affected modules completes with zero compilation errors and zero test failures. +- **SC-003**: Zero `$ref` references to `http://justice.gov.uk/core/courts/informantRegisterDocument/` remain in any command API schema or domain event schema for the informant-register flows. +- **SC-004**: The `results.prosecutor-results` query response schema validates against a payload containing the structured `verdict` object on an offence. +- **SC-005**: A unit test confirms replaying a pre-migration event (with flat `verdictCode`) does not throw an exception. + +## Development Constraints + +- **TDD is mandatory (Constitution Principle VIII)**: Every production code change MUST be preceded by a failing unit test that fails for the correct reason (assertion failure, not a compilation error). The commit history must show failing tests authored at or before the corresponding production code. This applies to: new/updated command handler methods, aggregate command and apply methods, event listener converters, event processor converters, query view methods, and all new local POJO types. +- **Missing tests first**: Before any schema migration or production code change is made, any gap in existing test coverage for the affected classes must be filled with failing tests. Existing tests for `InformantRegisterHandlerTest`, `ProsecutionAuthorityAggregateTest`, `InformantRegisterEventListenerTest`, `InformantRegisterEventProcessorTest`, and `ProsecutorResultsQueryViewTest` must be reviewed and supplemented where coverage is absent. +- **Three-layer discipline**: Every change touching a domain event must be assessed across all three layers — command side, event listener, event processor — with tests at each layer before production code. + +## Assumptions + +- `results.generate-informant-register-by-date.json` command schema is already self-contained (no core-domain reference) — no schema migration needed for that file. +- `InformantRegisterNotified`, `InformantRegisterNotifiedV2`, and `InformantRegisterNotificationIgnored` events are out of scope — they carry no offence data. +- Local Java types are hand-written POJOs (builder pattern, no Lombok) replacing the core-domain generated classes for the informant-register flows. +- The integration test (`InformantRegisterDocumentRequestIT`) is deferred — it will need updating separately after the unit-test-driven implementation is complete. +- `verdictType` is populated via a reference data lookup; if unavailable, it is omitted from the verdict object without failing the response. +- The plea value reporting issue noted in the Jira comments is out of scope for this ticket. diff --git a/specs/001-informant-register-local-schema/tasks.md b/specs/001-informant-register-local-schema/tasks.md new file mode 100644 index 000000000..3795c2635 --- /dev/null +++ b/specs/001-informant-register-local-schema/tasks.md @@ -0,0 +1,250 @@ +# Tasks: CIMD-3915 — Include Verdict in SJP Results + +**Input**: `specs/001-informant-register-local-schema/` +**Prerequisites**: plan.md ✓, spec.md ✓, research.md ✓, data-model.md ✓, contracts/ ✓ + +**TDD discipline (Constitution Principle VIII)**: Every production code task MUST be preceded by its paired failing-test task. Write the test, confirm it fails for the right reason (assertion, not compilation), then write the production code. + +## Format: `[ID] [P?] [Story?] Description` + +- **[P]**: Can run in parallel (different files, no shared dependencies) +- **[Story]**: User story — US1/US2/US3/US4 (from spec.md) +- Exact file paths included in all descriptions + +--- + +## Phase 1: Foundational — Contracts & Schemas (No Java) + +**Purpose**: All JSON schemas and subscription descriptor entries MUST exist before any Java is written (Constitution Principle I: Contract First). + +**⚠️ CRITICAL**: No Java task (Phase 2+) may start until T018 (build gate) passes. + +- [ ] T001 Create directory `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/` and write `verdict.json` with `$id: http://justice.gov.uk/results/courts/informantRegisterDocument/verdict.json` — properties: `verdictCode`, `verdictDate`, `verdictType` (all optional strings) +- [ ] T002 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json` — replaces flat `verdictCode` with `"verdict": { "$ref": "...verdict.json" }`; required: `offenceCode`, `orderIndex`, `offenceTitle` +- [ ] T003 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json` — properties: `resultText` (required), `cjsResultCode`, `resultData` ($ref to resultData) +- [ ] T004 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json` — properties: `amount`, `nextHearingDate`, `nextCourtLocation`, `durationValue`, `durationUnit`, `durationStartDate`, `durationEndDate`, `secondaryDurationValue`, `secondaryDurationUnit` (all optional strings) +- [ ] T005 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json` — required: `caseOrApplicationReference`; offences array $ref to offence schema +- [ ] T006 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json` — required: `name`, `address1`; optional: address2-5, postCode, dateOfBirth, nationality, firstName, lastName; prosecutionCasesOrApplications array +- [ ] T007 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json` — required: `courtRoom`, `hearingStartTime`, `defendants` (array, minItems 1) +- [ ] T008 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json` — required: `courtHouse`, `courtSessions` (array, minItems 1); optional: `ljaName` +- [ ] T009 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json` — required: `recipientName`, `emailAddress1`, `emailTemplateName`; optional: `emailAddress2` +- [ ] T010 Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json` — required: `registerDate`, `hearingDate`, `hearingId`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`, `fileName`, `hearingVenue`; $refs all local sub-schemas [after T001–T009] +- [ ] T011 Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json` — required: `startDate`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`; `hearingVenues` array $ref to local hearingVenue schema [after T010] +- [ ] T012 Write `results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json` — `$id: http://justice.gov.uk/results/courts/informant-register-recorded-v2.json`; required: `prosecutionAuthorityId`, `informantRegister` ($ref local documentRequest) [after T010] +- [ ] T013 Write `results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-generated-v2.json` — `$id: http://justice.gov.uk/results/courts/informant-register-generated-v2.json`; required: `informantRegisterDocumentRequests` (array $ref local documentRequest); optional: `systemGenerated` (boolean) [after T010] +- [ ] T014 Update `results-command/results-command-api/src/raml/json/schema/results.add-informant-register.json` — replace `$ref` to core-domain with `$ref: http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json` [after T010] +- [ ] T015 Update `results-query/results-query-api/src/raml/json/schema/results.prosecutor-results.json` — replace `$ref` to core-domain `prosecutorResult.json` with `$ref: http://justice.gov.uk/results/courts/informantRegisterDocument/prosecutorResult.json` [after T011] +- [ ] T016 Add two V2 subscription entries to `results-event/results-event-listener/src/yaml/subscriptions-descriptor.yaml`: `results.event.informant-register-recorded-v2` (schema_uri: `http://justice.gov.uk/results/courts/informant-register-recorded-v2.json`) and `results.event.informant-register-generated-v2` (schema_uri: `http://justice.gov.uk/results/courts/informant-register-generated-v2.json`) [after T012, T013] +- [ ] T017 Add V2 subscription entry to `results-event/results-event-processor/src/yaml/subscriptions-descriptor.yaml`: `results.event.informant-register-generated-v2` (schema_uri: `http://justice.gov.uk/results/courts/informant-register-generated-v2.json`) [after T013] +- [ ] T018 Build gate: `mvn clean install -DskipTests` on affected modules must succeed — confirm zero compilation errors before proceeding to any Java task + +**Checkpoint**: All contract artefacts exist and the project compiles. Phase 2 may now begin. + +--- + +## Phase 2: Foundational — Local POJO Types (TDD) + +**Purpose**: Hand-written local POJO types in `uk.gov.moj.cpp.results.domain.informant.model` (no Lombok, no Spring). These POJOs are required by US1, US2, US3, and US4 — they are blocking prerequisites for all user story phases. + +**⚠️ TDD**: Every test task must be written and confirmed to FAIL (assertion failure or compilation failure) BEFORE its paired production-code task begins. + +- [ ] T019 [P] Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictTest.java` — assert builder creates instance with `verdictCode`, `verdictDate`, `verdictType`; assert null fields tolerated; assert Jackson deserialization roundtrip from `{"verdictCode":"G","verdictDate":"2026-04-13","verdictType":"FOUND_GUILTY"}` preserves all fields +- [ ] T020 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/Verdict.java` — explicit constructor, getters, nested `Builder`; `@JsonCreator`/`@JsonProperty`; makes T019 green +- [ ] T021 [P] Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffenceTest.java` — assert builder sets `verdict` field (type `Verdict`); assert no `getVerdictCode()` method exists on the class; assert JSON with `verdict` object deserializes correctly; assert JSON with no `verdict` produces null verdict +- [ ] T022 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffence.java` — `verdict: Verdict` field; required fields `offenceCode`, `offenceTitle`, `orderIndex`; no `verdictCode` field; builder + getters; makes T021 green +- [ ] T023 [P] Write failing tests `InformantRegisterResultTest.java` and `InformantRegisterResultDataTest.java` in `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/` — assert builder/getters and JSON deserialization roundtrips +- [ ] T024 [P] Write `InformantRegisterResult.java` and `InformantRegisterResultData.java` in `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/` — builder pattern, Jackson annotations; makes T023 green +- [ ] T025 [P] Write failing tests `InformantRegisterCaseOrApplicationTest.java`, `InformantRegisterDefendantTest.java`, `InformantRegisterHearingTest.java`, `InformantRegisterHearingVenueTest.java`, `InformantRegisterRecipientTest.java` in `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/` — assert builders, getters, JSON deserialization roundtrips +- [ ] T026 [P] Write `InformantRegisterCaseOrApplication.java`, `InformantRegisterDefendant.java`, `InformantRegisterHearing.java`, `InformantRegisterHearingVenue.java`, `InformantRegisterRecipient.java` in `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/` — builder pattern, Jackson annotations; makes T025 green +- [ ] T027 Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequestTest.java` — assert builder sets all required fields; assert JSON deserialization of a nested document (hearingVenue → courtSessions → defendants → offences → verdict) produces correct object graph +- [ ] T028 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequest.java` — required: `registerDate` (ZonedDateTime), `hearingDate`, `hearingId`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`, `fileName`, `hearingVenue`; optional: `prosecutionAuthorityOuCode`, `majorCreditorCode`, `prosecutionAuthorityName`, `recipients`, `groupId`; builder + getters; makes T027 green +- [ ] T029 [P] Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResultTest.java` — assert builder sets `startDate`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`; optional fields nullable +- [ ] T030 [P] Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResult.java` — builder + getters; makes T029 green +- [ ] T031 Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2Test.java` — assert event name constant equals `"results.event.informant-register-recorded-v2"`; assert builder sets `prosecutionAuthorityId` (UUID) and `informantRegister` (local `InformantRegisterDocumentRequest`) +- [ ] T032 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2.java` — public static final `NAME = "results.event.informant-register-recorded-v2"`; fields: `prosecutionAuthorityId`, `informantRegister`; builder + getters; makes T031 green +- [ ] T033 Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2Test.java` — assert event name constant equals `"results.event.informant-register-generated-v2"`; assert builder sets `informantRegisterDocumentRequests` list and optional `systemGenerated` +- [ ] T034 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2.java` — public static final `NAME = "results.event.informant-register-generated-v2"`; fields: `informantRegisterDocumentRequests` (List), `systemGenerated` (Boolean); builder + getters; makes T033 green +- [ ] T035 Build gate: `mvn -pl results-domain/results-domain-common test` — all tests green + +**Checkpoint**: All local types exist and are tested. User story phases may now begin. + +--- + +## Phase 3: US1 — Add Informant Register Ingest (Priority: P1) 🎯 MVP + +**Goal**: The `results.add-informant-register` command handler accepts offences with a structured `verdict` object, emits `InformantRegisterRecordedV2`, and the `ProsecutionAuthorityAggregate` applies the V2 event using local types. + +**Independent Test**: Submit `results.add-informant-register` with an offence carrying a `verdict` object with `verdictCode="G"`, `verdictDate="2026-04-13"`, `verdictType="FOUND_GUILTY"` → confirm the emitted event is `results.event.informant-register-recorded-v2` and `payload.informantRegister.hearingVenue.courtSessions[0].defendants[0].prosecutionCasesOrApplications[0].offences[0].verdict.verdictCode` equals `"G"`. + +- [ ] T036 [US1] Gap-fill `results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java` — add missing scenarios: offence with no verdict (allowed), offence with null defendants (edge case), verify existing V1 tests still pass +- [ ] T037 [US1] Write failing test in `InformantRegisterHandlerTest.java` — `handleAddInformantRegisterToEventStream_withVerdictObject_shouldEmitV2RecordedEvent`: build a command envelope with local `InformantRegisterDocumentRequest` carrying an offence with `verdict.verdictCode="G"` → assert `eventRepository.create` called with event name `results.event.informant-register-recorded-v2` and payload contains `verdict.verdictCode = "G"`; confirm test fails (V2 event not yet emitted) +- [ ] T038 [US1] Update `results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java` — change `handleAddInformantRegisterToEventStream` to deserialize payload to local `InformantRegisterDocumentRequest` and emit `InformantRegisterRecordedV2`; keep or remove V1 emit per team decision (default: emit V2 only for new events, V1 kept for historical replay); makes T037 green +- [ ] T039 [US1] Gap-fill `results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java` — review existing `shouldReturnInformantRegisterNotified` and `shouldReturnInformantRegisterIgnored`; add missing scenarios for V1 generated event apply if not already covered +- [ ] T040 [US1] Write failing test in `ProsecutionAuthorityAggregateTest.java` — `apply_informantRegisterGeneratedV2_shouldSetRecipientsFromLocalTypes`: create `InformantRegisterGeneratedV2` event with local `InformantRegisterDocumentRequest` containing `InformantRegisterRecipient`; apply to aggregate; assert `informantRegisterRecipients` is populated correctly; confirm test fails +- [ ] T041 [US1] Update `results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java` — add `apply(InformantRegisterGeneratedV2 event)` method reading local `InformantRegisterDocumentRequest.getRecipients()`; update imports from core-domain to local types; makes T040 green +- [ ] T042 [US1] Build gate: `mvn -pl results-domain/results-domain-common,results-command/results-command-handler,results-domain/results-domain-aggregate -am test` — all tests green + +**Checkpoint**: US1 fully implemented and tested. The command ingest path accepts and stores verdict data. + +--- + +## Phase 4: US3 — Prosecutor Results Query (Priority: P1) + +**Goal**: The `results.prosecutor-results` query returns the structured `verdict` object on offences where a verdict was recorded; omits the field entirely when absent. + +**Independent Test**: Store an `InformantRegisterEntity` with a payload JSON containing an offence with `"verdict": {"verdictCode": "G", "verdictDate": "2026-04-13", "verdictType": "FOUND_GUILTY"}`; call `getProsecutorResults`; assert response JSON includes `hearingVenues[0].courtSessions[0].defendants[0].prosecutionCasesOrApplications[0].offences[0].verdict.verdictCode = "G"`. Store a second entity with an offence without a `verdict` key; assert response offence has no `verdict` field. + +- [ ] T043 [US3] Gap-fill `results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryViewTest.java` — add any missing scenarios for current behaviour (e.g., empty result set, multiple hearing venues, null defendants) +- [ ] T044 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceHasVerdict_shouldIncludeVerdictInResponse`: store `InformantRegisterEntity` with payload JSON containing offence with `verdict.verdictCode: "G"`; call query; assert returned `ProsecutorResult` includes `offence.verdict.verdictCode = "G"`, `offence.verdict.verdictDate` present; confirm test fails (current code uses core-domain type with no `verdict` field) +- [ ] T045 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceHasNoVerdict_shouldOmitVerdictField`: store entity with offence JSON that has no `verdict` key; assert response offence object serializes without `verdict` key (not `"verdict": null`) +- [ ] T046 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceVerdictIsNull_shouldOmitVerdictField`: store entity with `"verdict": null` in offence JSON; assert response offence has no `verdict` key +- [ ] T047 [US3] Update `results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java` — replace core-domain `InformantRegisterDocumentRequest` and `ProsecutorResult` with local types; ensure Jackson's `@JsonInclude(NON_NULL)` (or equivalent) on local `InformantRegisterOffence.verdict` omits absent/null verdict; makes T044–T046 green +- [ ] T048 [US3] Build gate: `mvn -pl results-query/results-query-view -am test` — all tests green + +**Checkpoint**: US3 fully implemented and tested. Query API returns structured verdict data. + +--- + +## Phase 5: US2 — Generate Informant Register (Priority: P2) + +**Goal**: `results.generate-informant-register` and `results.generate-informant-register-by-date` emit `InformantRegisterGeneratedV2`; the event listener and processor handle V2 generated events, propagating the structured verdict through to the CSV output. + +**Independent Test**: Trigger `results.generate-informant-register` → confirm emitted event is `results.event.informant-register-generated-v2`; send that event to the listener → confirm entity status set to GENERATED; send to processor → confirm CSV row `verdictCode` equals `"G"` when offence verdict is `FOUND_GUILTY`. + +- [ ] T049 [US2] Gap-fill `InformantRegisterHandlerTest.java` — add missing test: `processRequests_withLocalDocumentRequests_shouldEmitV2GeneratedEvent`; confirm this test currently fails (V2 not yet emitted by `processRequests`) +- [ ] T050 [US2] Write failing test in `InformantRegisterHandlerTest.java` — `processRequests_shouldEmitInformantRegisterGeneratedV2`: mock dependencies; confirm `processRequests` emits event with name `results.event.informant-register-generated-v2` and payload `informantRegisterDocumentRequests` list contains local `InformantRegisterDocumentRequest` with verdict data +- [ ] T051 [US2] Update `results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java` — change `processRequests` to emit `InformantRegisterGeneratedV2` using local types; makes T050 green +- [ ] T052 [US2] Gap-fill `results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/listener/InformantRegisterEventListenerTest.java` — review existing test coverage; add missing scenarios if any (e.g., missing test for status transition) +- [ ] T053 [US2] Write failing test in `InformantRegisterEventListenerTest.java` — `saveInformantRegisterV2_shouldSaveEntityFromLocalDocumentRequest`: build `results.event.informant-register-recorded-v2` envelope with local `InformantRegisterDocumentRequest`; assert listener saves `InformantRegisterEntity` with correct `prosecutionAuthorityId`, `registerDate`, and raw `payload` JSON; confirm fails (no V2 handler yet) +- [ ] T054 [US2] Write failing test in `InformantRegisterEventListenerTest.java` — `generateInformantRegisterV2_shouldSetEntityStatusToGenerated`: build `results.event.informant-register-generated-v2` envelope; assert entity `status` updated to GENERATED; confirm fails +- [ ] T055 [US2] Add `@Handles("results.event.informant-register-recorded-v2")` method `saveInformantRegisterV2` to `results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/listener/InformantRegisterEventListener.java`; deserializes local `InformantRegisterDocumentRequest`; reuses existing entity-save logic; makes T053 green +- [ ] T056 [US2] Add `@Handles("results.event.informant-register-generated-v2")` method `generateInformantRegisterV2` to `InformantRegisterEventListener.java`; updates entity status to GENERATED; makes T054 green +- [ ] T057 [US2] Gap-fill `results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java` — add missing coverage: `buildOffenceDetails` with offence that has a `verdict` object but `verdictCode = null` (should produce empty string); `buildOffenceDetails` with no verdict object at all +- [ ] T058 [US2] Write failing test in `InformantRegisterEventProcessorTest.java` — `generateInformantRegister_whenOffenceHasVerdictCode_shouldPopulateVerdictCodeInCsvRow`: build event with offence `verdict.verdictCode = "G"`; assert `InformantRegisterDocument.verdictCode = "G"` in the CSV model; confirm fails (current code calls `offence.getVerdictCode()` which does not exist on local type) +- [ ] T059 [US2] Update `buildOffenceDetails` in `results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java` — replace `offence.getVerdictCode()` with null-safe `offence.getVerdict() != null ? offence.getVerdict().getVerdictCode() : null`; makes T058 green +- [ ] T060 [US2] Write failing test in `InformantRegisterEventProcessorTest.java` — `generateInformantRegisterV2_shouldProduceSameCsvOutputAsV1Handler`: build `results.event.informant-register-generated-v2` envelope with the same data as a V1 test; assert the same CSV rows are produced; confirm fails (no V2 handler yet) +- [ ] T061 [US2] Add `@Handles("results.event.informant-register-generated-v2")` method `generateInformantRegisterV2` to `results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java`; delegates to same CSV-generation logic as V1 handler; makes T060 green +- [ ] T062 [US2] Build gate: `mvn -pl results-command/results-command-handler,results-event/results-event-listener,results-event/results-event-processor -am test` — all tests green + +**Checkpoint**: US2 fully implemented and tested. Generate flow emits V2 events; listener and processor handle them correctly. + +--- + +## Phase 6: US4 — Pre-migration Event Replay (Priority: P2) + +**Goal**: V1 events already in the event store (`informant-register-recorded`, `informant-register-generated`) replay through the listener without runtime errors, even though their offences carry a flat `verdictCode` string (old schema). + +**Independent Test**: Replay a V1 `informant-register-recorded` event JSON (old schema with flat `verdictCode: "G"` on an offence) through the V1 `saveInformantRegister` listener handler → confirm no exception thrown and entity saved correctly. + +- [ ] T063 [US4] Write failing test in `InformantRegisterEventListenerTest.java` — `saveInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow`: build V1 `informant-register-recorded` envelope JSON where offence has `"verdictCode": "G"` (flat string, old core-domain schema); send to V1 listener handler; assert no exception; assert entity saved with correct `prosecutionAuthorityId` +- [ ] T064 [US4] Confirm T063 passes without new production code — V1 handler uses core-domain types that already support flat `verdictCode`; document this confirmation explicitly (no production change needed) +- [ ] T065 [US4] Write failing test in `InformantRegisterEventListenerTest.java` — `generateInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow`: build V1 `informant-register-generated` envelope with offences carrying flat `verdictCode` string; send to V1 listener handler; assert no exception +- [ ] T066 [US4] Confirm T065 passes without new production code — document confirmation; no production change needed +- [ ] T067 [US4] Build gate: `mvn test` on all affected modules — full unit-test suite green + +**Checkpoint**: US4 verified. Pre-migration V1 events remain replayable with no production code changes. + +--- + +## Phase 7: Polish & Full Build + +**Purpose**: Final verification and code hygiene checks across all changes. + +- [ ] T068 Full build gate: `mvn clean install` — zero compilation errors, zero test failures across entire reactor +- [ ] T069 [P] Code hygiene review: verify all new/modified Java files in `results-domain-common`, `results-command-handler`, `results-domain-aggregate`, `results-event-listener`, `results-event-processor`, `results-query-view` have no wildcard imports, no `System.out`/`System.err`, no Spring annotations, no Lombok annotations, SLF4J logging only +- [ ] T070 [P] Schema-subscription symmetry check (Constitution Principle VI): verify `results.event.informant-register-recorded-v2.json` exists AND appears in listener `subscriptions-descriptor.yaml`; verify `results.event.informant-register-generated-v2.json` exists AND appears in both listener AND processor `subscriptions-descriptor.yaml`; verify zero `$ref` to `http://justice.gov.uk/core/courts/informantRegisterDocument/` remains in any command or event schema for informant-register flows + +**Checkpoint**: Build green, code clean, schema-subscription symmetry confirmed. Ready for code-reviewer and qa agents. + +--- + +## Dependencies & Execution Order + +### Phase Dependencies + +- **Phase 1 (Contracts)**: No dependencies — start immediately +- **Phase 2 (POJOs)**: Requires T018 (Phase 1 gate) — BLOCKS all user story phases +- **Phase 3 (US1)**: Requires T035 (Phase 2 gate) — first P1 story, no cross-story dependency +- **Phase 4 (US3)**: Requires T035 (Phase 2 gate) — can start in parallel with Phase 3 (different modules) +- **Phase 5 (US2)**: Requires T035 (Phase 2 gate) AND T042 (US1 gate, command handler V2 emit) — processor depends on V2 events being emitted +- **Phase 6 (US4)**: Requires T035 (Phase 2 gate) — tests V1 handlers (no new production code); can start after Phase 2 +- **Phase 7 (Polish)**: Requires T042, T048, T062, T067 — all user story gates passed + +### User Story Dependencies + +| Story | Priority | Depends on | Blocked by | +|-------|----------|-----------|------------| +| US1 — Add Ingest | P1 | Phase 2 complete | — | +| US3 — Query | P1 | Phase 2 complete | — | +| US2 — Generate | P2 | Phase 2 + US1 gate | T042 | +| US4 — Compat | P2 | Phase 2 complete | — | + +### Within Each Phase + +1. TDD rule: Test task written → confirmed failing → production code written → confirmed passing +2. In Phase 2: deepest types first (`Verdict` → `InformantRegisterOffence` → … → `InformantRegisterDocumentRequest` → V2 events) +3. In Phase 3: gap-fill → failing V2 test → production command handler → failing aggregate test → production aggregate +4. In Phase 4: gap-fill → failing verdict tests (T044–T046) → production view +5. In Phase 5: gap-fill → failing handler test → production handler → failing listener tests → production listener → failing processor tests → production processor + +--- + +## Parallel Opportunities + +### Phase 1 (Contracts) — run T002–T009 in parallel + +``` +T001 (verdict.json — no deps) +T002–T009 [all in parallel — different schema files, no cross-deps] +Then: T010, T011, T012, T013, T014, T015, T016, T017 (sequentially or grouped by dep) +Then: T018 (build gate) +``` + +### Phase 2 (POJOs) — run test+production pairs in parallel where types are independent + +``` +T019 + T020 (Verdict) in parallel with T021 + T022 (Offence — once Verdict exists) +T023 + T024 (Result + ResultData) in parallel with T025 + T026 (remaining mid-tier types) +T027 + T028 (DocumentRequest — depends on all above) +T029 + T030 (ProsecutorResult) in parallel with T031 + T032 (RecordedV2 event) +T033 + T034 (GeneratedV2 event) after T028 +T035 (gate) +``` + +### Phase 3 + Phase 4 — run in parallel (different modules) + +``` +After T035: + [Track A] Phase 3: T036–T041 (command handler + aggregate) + [Track B] Phase 4: T043–T047 (query view — different module, no dependency on Track A) + T042 (Track A gate) + T048 (Track B gate) — then Phase 5 can begin +``` + +--- + +## Implementation Strategy + +### MVP (US1 + US3 only — both P1) + +1. Phase 1: Contracts (T001–T018) +2. Phase 2: POJOs (T019–T035) +3. Phase 3: US1 — Add Ingest (T036–T042) +4. Phase 4: US3 — Query (T043–T048) +5. **STOP and VALIDATE**: `mvn test` on all P1 modules green; US1 and US3 independently testable +6. Deliver MVP — query returns verdict data for newly ingested records + +### Full Delivery (add P2 stories) + +7. Phase 5: US2 — Generate (T049–T062) in parallel with Phase 6: US4 — Compat (T063–T067) +8. Phase 7: Full build (T068–T070) + +--- + +## Notes + +- [P] tasks involve different files with no shared dependencies — safe to parallelize +- [US*] label maps task to user story for traceability +- TDD gate per story: each story's tests must be green before moving to the next +- Never skip T018 or T035 build gates — they are the Principle I and Phase 2 completeness checkpoints +- Commits: after each build gate (T018, T035, T042, T048, T062, T067, T068) at minimum +- Integration test (`InformantRegisterDocumentRequestIT`) deferred — not part of this task list (see spec.md Assumptions) From 58911d05ca6f0b4b775e80e472e3ea5ad48d1c8b Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Tue, 16 Jun 2026 13:44:54 +0100 Subject: [PATCH 02/22] docs(cimd-3915): apply speckit-analyze remediation findings - verdict.json: add dependencies constraint enforcing verdictCode/verdictDate co-presence (FR-004) - spec.md: record dual-emit decision (V2 only for new ingest, V1 retained for replay) - tasks.md: remove duplicate T049; add T071 (FR-004 rejection test), T072 (FR-008 warning-log test), T073 (empty-verdict omit test); relabel T063 as regression guard; add SC-003/SC-004 to T070; add phase mapping table; fix stale T049 reference in implementation strategy Co-Authored-By: Claude Sonnet 4.6 --- .../informantRegisterDocument/verdict.json | 4 +++ .../spec.md | 1 + .../tasks.md | 27 ++++++++++++++----- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/verdict.json b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/verdict.json index 33981af27..0607e4988 100644 --- a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/verdict.json +++ b/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/verdict.json @@ -17,5 +17,9 @@ "type": "string" } }, + "dependencies": { + "verdictCode": ["verdictDate"], + "verdictDate": ["verdictCode"] + }, "additionalProperties": false } diff --git a/specs/001-informant-register-local-schema/spec.md b/specs/001-informant-register-local-schema/spec.md index ef4d1693d..2acd61a37 100644 --- a/specs/001-informant-register-local-schema/spec.md +++ b/specs/001-informant-register-local-schema/spec.md @@ -153,3 +153,4 @@ Existing domain events stored before this change (carrying the old flat `verdict - The integration test (`InformantRegisterDocumentRequestIT`) is deferred — it will need updating separately after the unit-test-driven implementation is complete. - `verdictType` is populated via a reference data lookup; if unavailable, it is omitted from the verdict object without failing the response. - The plea value reporting issue noted in the Jira comments is out of scope for this ticket. +- **Dual-emit decision**: The command handler emits **V2 only** for all new ingest (both `add-informant-register` and `processRequests`). V1 events already persisted in the event store are handled by the existing V1 listener/processor handlers and are not re-emitted. V1 handlers are retained for backward-compatible replay (US4) but receive no new writes after this change. diff --git a/specs/001-informant-register-local-schema/tasks.md b/specs/001-informant-register-local-schema/tasks.md index 3795c2635..4958700e6 100644 --- a/specs/001-informant-register-local-schema/tasks.md +++ b/specs/001-informant-register-local-schema/tasks.md @@ -5,6 +5,17 @@ **TDD discipline (Constitution Principle VIII)**: Every production code task MUST be preceded by its paired failing-test task. Write the test, confirm it fails for the right reason (assertion, not compilation), then write the production code. +> **Phase mapping — plan.md vs tasks.md**: +> | tasks.md phase | plan.md phase | Scope | +> |---------------|---------------|-------| +> | Phase 1 (Contracts) | Phase 0 | JSON schemas + subscription descriptors | +> | Phase 2 (POJOs) | Phase 1 | Local Java types | +> | Phase 3 (US1) | Phases 2 + 3 | Command handler + aggregate | +> | Phase 4 (US3) | Phase 6 | Query view | +> | Phase 5 (US2) | Phases 4 + 5 | Event listener + processor | +> | Phase 6 (US4) | Phase 7 | Pre-migration backward compat | +> | Phase 7 (Polish) | Phase 8 | Full build + hygiene | + ## Format: `[ID] [P?] [Story?] Description` - **[P]**: Can run in parallel (different files, no shared dependencies) @@ -78,7 +89,8 @@ - [ ] T036 [US1] Gap-fill `results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java` — add missing scenarios: offence with no verdict (allowed), offence with null defendants (edge case), verify existing V1 tests still pass - [ ] T037 [US1] Write failing test in `InformantRegisterHandlerTest.java` — `handleAddInformantRegisterToEventStream_withVerdictObject_shouldEmitV2RecordedEvent`: build a command envelope with local `InformantRegisterDocumentRequest` carrying an offence with `verdict.verdictCode="G"` → assert `eventRepository.create` called with event name `results.event.informant-register-recorded-v2` and payload contains `verdict.verdictCode = "G"`; confirm test fails (V2 event not yet emitted) -- [ ] T038 [US1] Update `results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java` — change `handleAddInformantRegisterToEventStream` to deserialize payload to local `InformantRegisterDocumentRequest` and emit `InformantRegisterRecordedV2`; keep or remove V1 emit per team decision (default: emit V2 only for new events, V1 kept for historical replay); makes T037 green +- [ ] T071 [US1] Write failing test in `InformantRegisterHandlerTest.java` — `handleAddInformantRegisterToEventStream_withVerdictCodeButNoVerdictDate_shouldFail` (FR-004 co-dependency): build command envelope where an offence has `verdictCode: "G"` but no `verdictDate`; assert the command is rejected (schema validation failure or explicit handler guard); confirm test fails (handler not yet guarding this case). Note: `verdict.json` schema now enforces co-dependency via `dependencies` — verify whether the framework rejects this pre-handler via schema validation, and if so document that as the passing mechanism (no additional production code needed) +- [ ] T038 [US1] Update `results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java` — change `handleAddInformantRegisterToEventStream` to deserialize payload to local `InformantRegisterDocumentRequest` and emit `InformantRegisterRecordedV2`; emit V2 only (V1 retained in event store for historical replay per spec.md Assumptions); makes T037 green - [ ] T039 [US1] Gap-fill `results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java` — review existing `shouldReturnInformantRegisterNotified` and `shouldReturnInformantRegisterIgnored`; add missing scenarios for V1 generated event apply if not already covered - [ ] T040 [US1] Write failing test in `ProsecutionAuthorityAggregateTest.java` — `apply_informantRegisterGeneratedV2_shouldSetRecipientsFromLocalTypes`: create `InformantRegisterGeneratedV2` event with local `InformantRegisterDocumentRequest` containing `InformantRegisterRecipient`; apply to aggregate; assert `informantRegisterRecipients` is populated correctly; confirm test fails - [ ] T041 [US1] Update `results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java` — add `apply(InformantRegisterGeneratedV2 event)` method reading local `InformantRegisterDocumentRequest.getRecipients()`; update imports from core-domain to local types; makes T040 green @@ -98,7 +110,9 @@ - [ ] T044 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceHasVerdict_shouldIncludeVerdictInResponse`: store `InformantRegisterEntity` with payload JSON containing offence with `verdict.verdictCode: "G"`; call query; assert returned `ProsecutorResult` includes `offence.verdict.verdictCode = "G"`, `offence.verdict.verdictDate` present; confirm test fails (current code uses core-domain type with no `verdict` field) - [ ] T045 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceHasNoVerdict_shouldOmitVerdictField`: store entity with offence JSON that has no `verdict` key; assert response offence object serializes without `verdict` key (not `"verdict": null`) - [ ] T046 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceVerdictIsNull_shouldOmitVerdictField`: store entity with `"verdict": null` in offence JSON; assert response offence has no `verdict` key -- [ ] T047 [US3] Update `results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java` — replace core-domain `InformantRegisterDocumentRequest` and `ProsecutorResult` with local types; ensure Jackson's `@JsonInclude(NON_NULL)` (or equivalent) on local `InformantRegisterOffence.verdict` omits absent/null verdict; makes T044–T046 green +- [ ] T072 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenVerdictTypeUnavailable_shouldLogWarningAndStillReturnOffence` (FR-008): store entity with offence carrying `verdictCode: "G"` and `verdictDate`; configure verdict-type reference lookup to return empty/null for that code; call query; assert response offence includes `verdict.verdictCode` and `verdict.verdictDate` but omits `verdictType`; assert SLF4J logger captured a WARN message; confirm test fails (no warning log in current production code) +- [ ] T073 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenVerdictIsEmptyObject_shouldOmitVerdictField`: store entity with offence JSON `"verdict": {}`; call query; assert response offence has no `verdict` key at all (neither `{}` nor `null`); confirm test fails. Note: may require `@JsonInclude(NON_EMPTY)` rather than `NON_NULL` on `InformantRegisterOffence.verdict` +- [ ] T047 [US3] Update `results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java` — replace core-domain `InformantRegisterDocumentRequest` and `ProsecutorResult` with local types; add SLF4J WARN log when verdict-type lookup returns no result (FR-008); use `@JsonInclude(NON_EMPTY)` on `InformantRegisterOffence.verdict` to omit null and empty-object cases (T046 + T073); makes T044–T046, T072, T073 green - [ ] T048 [US3] Build gate: `mvn -pl results-query/results-query-view -am test` — all tests green **Checkpoint**: US3 fully implemented and tested. Query API returns structured verdict data. @@ -111,8 +125,7 @@ **Independent Test**: Trigger `results.generate-informant-register` → confirm emitted event is `results.event.informant-register-generated-v2`; send that event to the listener → confirm entity status set to GENERATED; send to processor → confirm CSV row `verdictCode` equals `"G"` when offence verdict is `FOUND_GUILTY`. -- [ ] T049 [US2] Gap-fill `InformantRegisterHandlerTest.java` — add missing test: `processRequests_withLocalDocumentRequests_shouldEmitV2GeneratedEvent`; confirm this test currently fails (V2 not yet emitted by `processRequests`) -- [ ] T050 [US2] Write failing test in `InformantRegisterHandlerTest.java` — `processRequests_shouldEmitInformantRegisterGeneratedV2`: mock dependencies; confirm `processRequests` emits event with name `results.event.informant-register-generated-v2` and payload `informantRegisterDocumentRequests` list contains local `InformantRegisterDocumentRequest` with verdict data +- [ ] T050 [US2] Gap-fill `InformantRegisterHandlerTest.java` then write failing test `processRequests_shouldEmitInformantRegisterGeneratedV2`: review existing `processRequests` test coverage; add missing scenarios (e.g., empty document-requests list, null recipients); then write the V2 failing test — mock dependencies, confirm `processRequests` emits event with name `results.event.informant-register-generated-v2` and payload `informantRegisterDocumentRequests` list contains local `InformantRegisterDocumentRequest` with verdict data; confirm test currently fails (V2 not yet emitted) - [ ] T051 [US2] Update `results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java` — change `processRequests` to emit `InformantRegisterGeneratedV2` using local types; makes T050 green - [ ] T052 [US2] Gap-fill `results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/listener/InformantRegisterEventListenerTest.java` — review existing test coverage; add missing scenarios if any (e.g., missing test for status transition) - [ ] T053 [US2] Write failing test in `InformantRegisterEventListenerTest.java` — `saveInformantRegisterV2_shouldSaveEntityFromLocalDocumentRequest`: build `results.event.informant-register-recorded-v2` envelope with local `InformantRegisterDocumentRequest`; assert listener saves `InformantRegisterEntity` with correct `prosecutionAuthorityId`, `registerDate`, and raw `payload` JSON; confirm fails (no V2 handler yet) @@ -136,7 +149,7 @@ **Independent Test**: Replay a V1 `informant-register-recorded` event JSON (old schema with flat `verdictCode: "G"` on an offence) through the V1 `saveInformantRegister` listener handler → confirm no exception thrown and entity saved correctly. -- [ ] T063 [US4] Write failing test in `InformantRegisterEventListenerTest.java` — `saveInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow`: build V1 `informant-register-recorded` envelope JSON where offence has `"verdictCode": "G"` (flat string, old core-domain schema); send to V1 listener handler; assert no exception; assert entity saved with correct `prosecutionAuthorityId` +- [ ] T063 [US4] Write passing regression test in `InformantRegisterEventListenerTest.java` — `saveInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow`: build V1 `informant-register-recorded` envelope JSON where offence has `"verdictCode": "G"` (flat string, old core-domain schema); send to V1 listener handler; assert no exception; assert entity saved with correct `prosecutionAuthorityId`. This test is expected to PASS without new production code — it is a backward-compat regression guard, not a TDD failing test - [ ] T064 [US4] Confirm T063 passes without new production code — V1 handler uses core-domain types that already support flat `verdictCode`; document this confirmation explicitly (no production change needed) - [ ] T065 [US4] Write failing test in `InformantRegisterEventListenerTest.java` — `generateInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow`: build V1 `informant-register-generated` envelope with offences carrying flat `verdictCode` string; send to V1 listener handler; assert no exception - [ ] T066 [US4] Confirm T065 passes without new production code — document confirmation; no production change needed @@ -152,7 +165,7 @@ - [ ] T068 Full build gate: `mvn clean install` — zero compilation errors, zero test failures across entire reactor - [ ] T069 [P] Code hygiene review: verify all new/modified Java files in `results-domain-common`, `results-command-handler`, `results-domain-aggregate`, `results-event-listener`, `results-event-processor`, `results-query-view` have no wildcard imports, no `System.out`/`System.err`, no Spring annotations, no Lombok annotations, SLF4J logging only -- [ ] T070 [P] Schema-subscription symmetry check (Constitution Principle VI): verify `results.event.informant-register-recorded-v2.json` exists AND appears in listener `subscriptions-descriptor.yaml`; verify `results.event.informant-register-generated-v2.json` exists AND appears in both listener AND processor `subscriptions-descriptor.yaml`; verify zero `$ref` to `http://justice.gov.uk/core/courts/informantRegisterDocument/` remains in any command or event schema for informant-register flows +- [ ] T070 [P] Schema-subscription symmetry check (Constitution Principle VI + SC-003): verify `results.event.informant-register-recorded-v2.json` exists AND appears in listener `subscriptions-descriptor.yaml`; verify `results.event.informant-register-generated-v2.json` exists AND appears in both listener AND processor `subscriptions-descriptor.yaml`; verify zero `$ref` to `http://justice.gov.uk/core/courts/informantRegisterDocument/` remains in any command or event schema for informant-register flows (SC-003); confirm `results.prosecutor-results.json` schema accepts a sample payload containing a `verdict` sub-object with `verdictCode`, `verdictDate`, `verdictType` (SC-004) **Checkpoint**: Build green, code clean, schema-subscription symmetry confirmed. Ready for code-reviewer and qa agents. @@ -235,7 +248,7 @@ After T035: ### Full Delivery (add P2 stories) -7. Phase 5: US2 — Generate (T049–T062) in parallel with Phase 6: US4 — Compat (T063–T067) +7. Phase 5: US2 — Generate (T050–T062) in parallel with Phase 6: US4 — Compat (T063–T067) 8. Phase 7: Full build (T068–T070) --- From 413e0c2a4abb96ae8159550f788e9ab9c0c2973f Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Tue, 16 Jun 2026 15:49:49 +0100 Subject: [PATCH 03/22] CIMD-3915 : verdict implementation --- .../handler/InformantRegisterHandler.java | 15 +- .../results/command/util/DefendantMapper.java | 2 +- .../ProsecutionCaseOrApplicationMapper.java | 8 +- .../handler/InformantRegisterHandlerTest.java | 57 ++++-- .../ProsecutionAuthorityAggregate.java | 32 +++- .../ProsecutionAuthorityAggregateTest.java | 42 ++++- .../event/InformantRegisterGeneratedV2.java | 45 +++++ .../event/InformantRegisterRecordedV2.java | 45 +++++ .../InformantRegisterCaseOrApplication.java | 91 ++++++++++ .../model/InformantRegisterDefendant.java | 115 ++++++++++++ .../InformantRegisterDocumentRequest.java | 103 +++++++++++ .../model/InformantRegisterHearing.java | 64 +++++++ .../model/InformantRegisterHearingVenue.java | 64 +++++++ .../model/InformantRegisterOffence.java | 119 +++++++++++++ .../model/InformantRegisterRecipient.java | 79 +++++++++ .../model/InformantRegisterResult.java | 62 +++++++ .../model/InformantRegisterResultData.java | 142 +++++++++++++++ .../informant/model/ProsecutorResult.java | 79 +++++++++ .../domain/informant/model/Verdict.java | 62 +++++++ .../InformantRegisterGeneratedV2Test.java | 61 +++++++ .../InformantRegisterRecordedV2Test.java | 43 +++++ ...nformantRegisterCaseOrApplicationTest.java | 44 +++++ .../model/InformantRegisterDefendantTest.java | 63 +++++++ .../InformantRegisterDocumentRequestTest.java | 75 ++++++++ .../model/InformantRegisterHearingTest.java | 41 +++++ .../InformantRegisterHearingVenueTest.java | 50 ++++++ .../model/InformantRegisterOffenceTest.java | 66 +++++++ .../model/InformantRegisterRecipientTest.java | 44 +++++ .../InformantRegisterResultDataTest.java | 47 +++++ .../model/InformantRegisterResultTest.java | 51 ++++++ .../informant/model/ProsecutorResultTest.java | 37 ++++ .../domain/informant/model/VerdictTest.java | 56 ++++++ .../event/InformantRegisterEventListener.java | 49 +++++ .../InformantRegisterEventListenerTest.java | 167 ++++++++++++++++++ .../InformantRegisterEventProcessor.java | 131 ++++++++++++++ .../InformantRegisterEventProcessorTest.java | 100 ++++++++++- .../InformantRegisterDocumentRequestIT.java | 20 +++ ...nformantRegisterDocumentRequestHelper.java | 12 ++ ...egister-document-request-with-verdict.json | 102 +++++++++++ .../view/ProsecutorResultsQueryView.java | 7 +- .../view/ProsecutorResultsQueryViewTest.java | 120 ++++++++++++- .../tasks.md | 27 +-- 42 files changed, 2571 insertions(+), 68 deletions(-) create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplication.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendant.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequest.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearing.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenue.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffence.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipient.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResult.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultData.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResult.java create mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/Verdict.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2Test.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2Test.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplicationTest.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendantTest.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequestTest.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingTest.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenueTest.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffenceTest.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipientTest.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultDataTest.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultTest.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResultTest.java create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictTest.java create mode 100644 results-integration-test/src/test/resources/json/informant-register/results.add-informant-register-document-request-with-verdict.json diff --git a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java index c7158641a..4299a2c10 100644 --- a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java +++ b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java @@ -13,12 +13,12 @@ import static uk.gov.moj.cpp.domains.constant.RegisterStatus.RECORDED; import static uk.gov.moj.cpp.results.command.util.DefendantMapper.getDefendants; -import uk.gov.justice.core.courts.InformantRegisterRecorded; import uk.gov.justice.core.courts.ProsecutionCase; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDefendant; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest; +import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; +import uk.gov.moj.cpp.results.domain.event.InformantRegisterRecordedV2; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; import uk.gov.justice.results.courts.GenerateInformantRegister; -import uk.gov.justice.results.courts.InformantRegisterGenerated; import uk.gov.justice.results.courts.NotifyInformantRegister; import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter; import uk.gov.justice.services.common.converter.StringToJsonObjectConverter; @@ -95,7 +95,10 @@ public void handleAddInformantRegisterToEventStream(final Envelope events = Stream.of(new InformantRegisterRecorded(informantRegisterDocumentRequest, prosecutionAuthorityId)); + final Stream events = Stream.of(InformantRegisterRecordedV2.informantRegisterRecordedV2() + .withProsecutionAuthorityId(prosecutionAuthorityId) + .withInformantRegister(informantRegisterDocumentRequest) + .build()); appendEventsToStream(envelope, eventStream, events); } @@ -163,7 +166,7 @@ private void processRequests(final UUID informantRegisterId, final List jsonObjectToObjectConverter.convert(informantRegister, InformantRegisterDocumentRequest.class)) .toList(); - final Stream events = Stream.of(InformantRegisterGenerated.informantRegisterGenerated() + final Stream events = Stream.of(InformantRegisterGeneratedV2.informantRegisterGeneratedV2() .withInformantRegisterDocumentRequests(informantRegisterDocumentRequests) .withSystemGenerated(systemGenerated) .build()); diff --git a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/DefendantMapper.java b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/DefendantMapper.java index daa9be564..8becebf26 100644 --- a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/DefendantMapper.java +++ b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/DefendantMapper.java @@ -10,7 +10,7 @@ import uk.gov.justice.core.courts.Defendant; import uk.gov.justice.core.courts.PersonDefendant; import uk.gov.justice.core.courts.ProsecutionCase; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDefendant; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; import java.time.format.DateTimeFormatter; import java.util.ArrayList; diff --git a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/ProsecutionCaseOrApplicationMapper.java b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/ProsecutionCaseOrApplicationMapper.java index eb62aebe3..b558df468 100644 --- a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/ProsecutionCaseOrApplicationMapper.java +++ b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/ProsecutionCaseOrApplicationMapper.java @@ -7,10 +7,10 @@ import uk.gov.justice.core.courts.Defendant; import uk.gov.justice.core.courts.ProsecutionCase; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterCaseOrApplication; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDefendant; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterOffence; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterResult; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult; import java.util.ArrayList; import java.util.List; diff --git a/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java b/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java index f6521f79d..8705a243a 100644 --- a/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java +++ b/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java @@ -15,8 +15,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; -import static uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterRecipient.informantRegisterRecipient; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient.informantRegisterRecipient; import static uk.gov.justice.services.core.annotation.Component.COMMAND_HANDLER; import static uk.gov.justice.services.messaging.Envelope.envelopeFrom; import static uk.gov.justice.services.messaging.Envelope.metadataFrom; @@ -31,16 +31,18 @@ import static uk.gov.justice.services.test.utils.core.reflection.ReflectionUtil.setField; import static uk.gov.moj.cpp.domains.InformantRegisterHelper.getInformantRegisterStreamId; -import uk.gov.justice.core.courts.InformantRegisterRecorded; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterCaseOrApplication; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDefendant; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearing; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearingVenue; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterOffence; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterRecipient; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterResult; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterResultData; +import uk.gov.moj.cpp.results.domain.event.InformantRegisterRecordedV2; +import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResultData; +import uk.gov.moj.cpp.results.domain.informant.model.Verdict; import uk.gov.justice.results.courts.GenerateInformantRegister; import uk.gov.justice.results.courts.InformantRegisterGenerated; import uk.gov.justice.results.courts.InformantRegisterNotificationIgnored; @@ -138,7 +140,7 @@ public class InformantRegisterHandlerTest { private final StringToJsonObjectConverter stringToJsonObjectConverter = new StringToJsonObjectConverter(); @Spy - private Enveloper enveloper = EnveloperFactory.createEnveloperWithEvents(InformantRegisterRecorded.class, InformantRegisterGenerated.class, InformantRegisterNotified.class, InformantRegisterNotifiedV2.class, InformantRegisterNotificationIgnored.class); + private Enveloper enveloper = EnveloperFactory.createEnveloperWithEvents(InformantRegisterGenerated.class, InformantRegisterNotified.class, InformantRegisterNotifiedV2.class, InformantRegisterNotificationIgnored.class, InformantRegisterRecordedV2.class, InformantRegisterGeneratedV2.class); @BeforeEach public void setup() { @@ -167,7 +169,7 @@ public void shouldProcessCommand() throws Exception { assertThat(envelopeStream, streamContaining( jsonEnvelope( - metadata().withName("results.event.informant-register-recorded"), + metadata().withName("results.event.informant-register-recorded-v2"), JsonEnvelopePayloadMatcher.payload().isJson(allOf( withJsonPath("$.prosecutionAuthorityId", is(PROSECUTION_AUTHORITY_ID.toString())), withJsonPath("$.informantRegister.prosecutionAuthorityId", is(PROSECUTION_AUTHORITY_ID.toString())), @@ -189,7 +191,7 @@ public void shouldProcessCommandForGroupCases() throws Exception { MatcherAssert.assertThat(envelopeStream, streamContaining( jsonEnvelope( - metadata().withName("results.event.informant-register-recorded"), + metadata().withName("results.event.informant-register-recorded-v2"), JsonEnvelopePayloadMatcher.payload().isJson(allOf( withJsonPath("$.prosecutionAuthorityId", is(PROSECUTION_AUTHORITY_ID.toString())), withJsonPath("$.informantRegister", notNullValue()), @@ -250,7 +252,7 @@ public void shouldProcessCommandForGroupCasesWithoutDefAndCaseResults() throws E MatcherAssert.assertThat(envelopeStream, streamContaining( jsonEnvelope( - metadata().withName("results.event.informant-register-recorded"), + metadata().withName("results.event.informant-register-recorded-v2"), JsonEnvelopePayloadMatcher.payload().isJson(allOf( withJsonPath("$.prosecutionAuthorityId", is(PROSECUTION_AUTHORITY_ID.toString())), withJsonPath("$.informantRegister", notNullValue()), @@ -282,6 +284,25 @@ public void shouldProcessCommandForGroupCasesWithoutDefAndCaseResults() throws E ); } + @Test + public void handleAddInformantRegisterToEventStream_withVerdictObject_shouldEmitV2RecordedEvent() throws Exception { + final UUID informantRegisterId = getInformantRegisterStreamId(PROSECUTION_AUTHORITY_ID.toString(), REGISTER_DATE.toLocalDate().toString()); + when(eventSource.getStreamById(informantRegisterId)).thenReturn(eventStream); + + informantRegisterHandler.handleAddInformantRegisterToEventStream(buildEnvelope()); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("results.event.informant-register-recorded-v2"), + JsonEnvelopePayloadMatcher.payload().isJson(allOf( + withJsonPath("$.prosecutionAuthorityId", is(PROSECUTION_AUTHORITY_ID.toString())), + withJsonPath("$.informantRegister.prosecutionAuthorityId", is(PROSECUTION_AUTHORITY_ID.toString())) + )) + ) + )); + } + @Test public void generateInformantRegister() throws EventStreamException { final Envelope generateInformantRegisterEnvelope = prepareEnvelope(); @@ -412,7 +433,7 @@ private void verifyInformantRegisterDocumentRequestHandlerResults() throws Event assertThat(envelopeStream, streamContaining( jsonEnvelope( metadata() - .withName("results.event.informant-register-generated"), + .withName("results.event.informant-register-generated-v2"), JsonEnvelopePayloadMatcher.payload().isJson(anyOf( withJsonPath("$.informantRegisterDocumentRequests.length()", is(greaterThan(0))) ) @@ -506,7 +527,7 @@ private final List getCaseOrApplications(fin .withOrderIndex(1) .withOffenceTitle("offenceTitle_Main_" + i) .withOffenceCode("offenceCode_Main_" + i) - .withVerdictCode("verdictCode_Main_" + i) + .withVerdict(Verdict.verdict().withVerdictCode("verdictCode_Main_" + i).build()) .withOriginatingCaseUrn("originatingCaseUrn_Main_" + i) .withPleaValue("pleaValue_Main_" + i) .withOffenceResults(getResults(RESULT_TYPE_OFFENCE, i, 1)) diff --git a/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java b/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java index e13c236d4..cc907e888 100644 --- a/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java +++ b/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java @@ -11,13 +11,14 @@ import uk.gov.justice.core.courts.InformantRegisterRecorded; import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterRecipient; import uk.gov.justice.domain.aggregate.Aggregate; import uk.gov.justice.results.courts.InformantRegisterGenerated; import uk.gov.justice.results.courts.InformantRegisterNotificationIgnored; import uk.gov.justice.results.courts.InformantRegisterNotified; import uk.gov.justice.results.courts.InformantRegisterNotifiedV2; import uk.gov.justice.results.courts.NotifyInformantRegister; +import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient; import java.util.Collections; import java.util.List; @@ -38,7 +39,22 @@ public Object apply(final Object event) { informantRegisterDocumentRequest -> nonNull(informantRegisterDocumentRequest.getRecipients()) && !informantRegisterDocumentRequest.getRecipients().isEmpty()) .collect(Collectors.toList()); if (isNotEmpty(informantRegisterWithRecipients)) { - this.informantRegisterRecipients = informantRegisterWithRecipients.get(0).getRecipients(); + this.informantRegisterRecipients = informantRegisterWithRecipients.get(0).getRecipients().stream() + .map(r -> InformantRegisterRecipient.informantRegisterRecipient() + .withRecipientName(r.getRecipientName()) + .withEmailAddress1(r.getEmailAddress1()) + .withEmailAddress2(r.getEmailAddress2()) + .withEmailTemplateName(r.getEmailTemplateName()) + .build()) + .collect(Collectors.toList()); + } + }), + when(InformantRegisterGeneratedV2.class).apply(e -> { + final List informantRegisterWithRecipients = e.getInformantRegisterDocumentRequests().stream().filter( + informantRegisterDocumentRequest -> nonNull(informantRegisterDocumentRequest.getRecipients()) && !informantRegisterDocumentRequest.getRecipients().isEmpty()) + .collect(Collectors.toList()); + if (isNotEmpty(informantRegisterWithRecipients)) { + this.informantRegisterRecipients = Collections.unmodifiableList(informantRegisterWithRecipients.get(0).getRecipients()); } }), otherwiseDoNothing() @@ -54,7 +70,15 @@ public Stream notifyProsecutingAuthority(final NotifyInformantRegister n .withProsecutionAuthorityId(notifyInformantRegister.getProsecutionAuthorityId()).build())); } - return apply(Stream.of(InformantRegisterNotifiedV2.informantRegisterNotifiedV2().withRecipients(informantRegisterRecipients) + final List coreRecipients = informantRegisterRecipients.stream() + .map(r -> uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterRecipient.informantRegisterRecipient() + .withRecipientName(r.getRecipientName()) + .withEmailAddress1(r.getEmailAddress1()) + .withEmailAddress2(r.getEmailAddress2()) + .withEmailTemplateName(r.getEmailTemplateName()) + .build()) + .collect(Collectors.toList()); + return apply(Stream.of(InformantRegisterNotifiedV2.informantRegisterNotifiedV2().withRecipients(coreRecipients) .withFileId(notifyInformantRegister.getFileId()) .withTemplateId(notifyInformantRegister.getTemplateId()) .withProsecutionAuthorityId(notifyInformantRegister.getProsecutionAuthorityId()) @@ -62,7 +86,7 @@ public Stream notifyProsecutingAuthority(final NotifyInformantRegister n .build())); } - public void setInformantRegisterRecipients(List informantRegisterRecipients) { + public void setInformantRegisterRecipients(final List informantRegisterRecipients) { this.informantRegisterRecipients = Collections.unmodifiableList(informantRegisterRecipients); } diff --git a/results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java b/results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java index c2512b1d4..2f695cb50 100644 --- a/results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java +++ b/results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java @@ -6,19 +6,17 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; -import static uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearingVenue.informantRegisterHearingVenue; -import static uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterRecipient.informantRegisterRecipient; +import static org.hamcrest.Matchers.hasSize; import static uk.gov.justice.results.courts.NotifyInformantRegister.notifyInformantRegister; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient.informantRegisterRecipient; -import uk.gov.justice.core.courts.InformantRegisterRecorded; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterRecipient; -import uk.gov.justice.results.courts.InformantRegisterGenerated; import uk.gov.justice.results.courts.InformantRegisterNotificationIgnored; -import uk.gov.justice.results.courts.InformantRegisterNotified; import uk.gov.justice.results.courts.InformantRegisterNotifiedV2; import uk.gov.justice.results.courts.NotifyInformantRegister; +import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient; import java.util.List; import java.util.UUID; @@ -56,6 +54,34 @@ public void shouldReturnInformantRegisterNotified() { assertThat(object.getClass(), is(equalTo(InformantRegisterNotifiedV2.class))); } + @Test + public void apply_informantRegisterGeneratedV2_shouldSetRecipientsFromLocalTypes() { + final InformantRegisterRecipient recipient = informantRegisterRecipient() + .withRecipientName("Jane") + .withEmailAddress1("jane@hmcts.net") + .withEmailTemplateName("template") + .build(); + final InformantRegisterDocumentRequest documentRequest = informantRegisterDocumentRequest() + .withRecipients(singletonList(recipient)) + .build(); + final InformantRegisterGeneratedV2 event = InformantRegisterGeneratedV2.informantRegisterGeneratedV2() + .withInformantRegisterDocumentRequests(singletonList(documentRequest)) + .withSystemGenerated(false) + .build(); + + aggregate.apply(event); + + final NotifyInformantRegister notifyInformantRegister = notifyInformantRegister() + .withProsecutionAuthorityId(randomUUID()) + .withTemplateId("template Id") + .withFileId(randomUUID()) + .build(); + + final List eventStream = aggregate.notifyProsecutingAuthority(notifyInformantRegister).collect(toList()); + assertThat(eventStream.size(), is(1)); + assertThat(eventStream.get(0).getClass(), is(equalTo(InformantRegisterNotifiedV2.class))); + } + @Test public void shouldReturnInformantRegisterIgnored() { final NotifyInformantRegister notifyInformantRegister = notifyInformantRegister() diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2.java new file mode 100644 index 000000000..0d28c26ff --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2.java @@ -0,0 +1,45 @@ +package uk.gov.moj.cpp.results.domain.event; + +import uk.gov.justice.domain.annotation.Event; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +@Event("results.event.informant-register-generated-v2") +public class InformantRegisterGeneratedV2 { + + public static final String NAME = "results.event.informant-register-generated-v2"; + + private final List informantRegisterDocumentRequests; + private final Boolean systemGenerated; + + @JsonCreator + public InformantRegisterGeneratedV2( + @JsonProperty("informantRegisterDocumentRequests") final List informantRegisterDocumentRequests, + @JsonProperty("systemGenerated") final Boolean systemGenerated) { + this.informantRegisterDocumentRequests = informantRegisterDocumentRequests; + this.systemGenerated = systemGenerated; + } + + public List getInformantRegisterDocumentRequests() { return informantRegisterDocumentRequests; } + public Boolean getSystemGenerated() { return systemGenerated; } + + public static Builder informantRegisterGeneratedV2() { + return new Builder(); + } + + public static class Builder { + private List informantRegisterDocumentRequests; + private Boolean systemGenerated; + + public Builder withInformantRegisterDocumentRequests(final List informantRegisterDocumentRequests) { this.informantRegisterDocumentRequests = informantRegisterDocumentRequests; return this; } + public Builder withSystemGenerated(final Boolean systemGenerated) { this.systemGenerated = systemGenerated; return this; } + + public InformantRegisterGeneratedV2 build() { + return new InformantRegisterGeneratedV2(informantRegisterDocumentRequests, systemGenerated); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2.java new file mode 100644 index 000000000..debe8bdb6 --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2.java @@ -0,0 +1,45 @@ +package uk.gov.moj.cpp.results.domain.event; + +import uk.gov.justice.domain.annotation.Event; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; + +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +@Event("results.event.informant-register-recorded-v2") +public class InformantRegisterRecordedV2 { + + public static final String NAME = "results.event.informant-register-recorded-v2"; + + private final UUID prosecutionAuthorityId; + private final InformantRegisterDocumentRequest informantRegister; + + @JsonCreator + public InformantRegisterRecordedV2( + @JsonProperty("prosecutionAuthorityId") final UUID prosecutionAuthorityId, + @JsonProperty("informantRegister") final InformantRegisterDocumentRequest informantRegister) { + this.prosecutionAuthorityId = prosecutionAuthorityId; + this.informantRegister = informantRegister; + } + + public UUID getProsecutionAuthorityId() { return prosecutionAuthorityId; } + public InformantRegisterDocumentRequest getInformantRegister() { return informantRegister; } + + public static Builder informantRegisterRecordedV2() { + return new Builder(); + } + + public static class Builder { + private UUID prosecutionAuthorityId; + private InformantRegisterDocumentRequest informantRegister; + + public Builder withProsecutionAuthorityId(final UUID prosecutionAuthorityId) { this.prosecutionAuthorityId = prosecutionAuthorityId; return this; } + public Builder withInformantRegister(final InformantRegisterDocumentRequest informantRegister) { this.informantRegister = informantRegister; return this; } + + public InformantRegisterRecordedV2 build() { + return new InformantRegisterRecordedV2(prosecutionAuthorityId, informantRegister); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplication.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplication.java new file mode 100644 index 000000000..f9cc30a9c --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplication.java @@ -0,0 +1,91 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class InformantRegisterCaseOrApplication { + + private final String caseOrApplicationReference; + private final String arrestSummonsNumber; + private final String applicationParticulars; + private final List offences; + private final List results; + + @JsonCreator + public InformantRegisterCaseOrApplication( + @JsonProperty("caseOrApplicationReference") final String caseOrApplicationReference, + @JsonProperty("arrestSummonsNumber") final String arrestSummonsNumber, + @JsonProperty("applicationParticulars") final String applicationParticulars, + @JsonProperty("offences") final List offences, + @JsonProperty("results") final List results) { + this.caseOrApplicationReference = caseOrApplicationReference; + this.arrestSummonsNumber = arrestSummonsNumber; + this.applicationParticulars = applicationParticulars; + this.offences = offences; + this.results = results; + } + + public String getCaseOrApplicationReference() { + return caseOrApplicationReference; + } + + public String getArrestSummonsNumber() { + return arrestSummonsNumber; + } + + public String getApplicationParticulars() { + return applicationParticulars; + } + + public List getOffences() { + return offences; + } + + public List getResults() { + return results; + } + + public static Builder informantRegisterCaseOrApplication() { + return new Builder(); + } + + public static class Builder { + private String caseOrApplicationReference; + private String arrestSummonsNumber; + private String applicationParticulars; + private List offences; + private List results; + + public Builder withCaseOrApplicationReference(final String caseOrApplicationReference) { + this.caseOrApplicationReference = caseOrApplicationReference; + return this; + } + + public Builder withArrestSummonsNumber(final String arrestSummonsNumber) { + this.arrestSummonsNumber = arrestSummonsNumber; + return this; + } + + public Builder withApplicationParticulars(final String applicationParticulars) { + this.applicationParticulars = applicationParticulars; + return this; + } + + public Builder withOffences(final List offences) { + this.offences = offences; + return this; + } + + public Builder withResults(final List results) { + this.results = results; + return this; + } + + public InformantRegisterCaseOrApplication build() { + return new InformantRegisterCaseOrApplication(caseOrApplicationReference, + arrestSummonsNumber, applicationParticulars, offences, results); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendant.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendant.java new file mode 100644 index 000000000..d646a0246 --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendant.java @@ -0,0 +1,115 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class InformantRegisterDefendant { + + private final String name; + private final String address1; + private final String address2; + private final String address3; + private final String address4; + private final String address5; + private final String postCode; + private final String dateOfBirth; + private final String nationality; + private final String title; + private final String firstName; + private final String lastName; + private final List prosecutionCasesOrApplications; + private final List results; + + @JsonCreator + public InformantRegisterDefendant( + @JsonProperty("name") final String name, + @JsonProperty("address1") final String address1, + @JsonProperty("address2") final String address2, + @JsonProperty("address3") final String address3, + @JsonProperty("address4") final String address4, + @JsonProperty("address5") final String address5, + @JsonProperty("postCode") final String postCode, + @JsonProperty("dateOfBirth") final String dateOfBirth, + @JsonProperty("nationality") final String nationality, + @JsonProperty("title") final String title, + @JsonProperty("firstName") final String firstName, + @JsonProperty("lastName") final String lastName, + @JsonProperty("prosecutionCasesOrApplications") final List prosecutionCasesOrApplications, + @JsonProperty("results") final List results) { + this.name = name; + this.address1 = address1; + this.address2 = address2; + this.address3 = address3; + this.address4 = address4; + this.address5 = address5; + this.postCode = postCode; + this.dateOfBirth = dateOfBirth; + this.nationality = nationality; + this.title = title; + this.firstName = firstName; + this.lastName = lastName; + this.prosecutionCasesOrApplications = prosecutionCasesOrApplications; + this.results = results; + } + + public String getName() { return name; } + public String getAddress1() { return address1; } + public String getAddress2() { return address2; } + public String getAddress3() { return address3; } + public String getAddress4() { return address4; } + public String getAddress5() { return address5; } + public String getPostCode() { return postCode; } + public String getDateOfBirth() { return dateOfBirth; } + public String getNationality() { return nationality; } + public String getTitle() { return title; } + public String getFirstName() { return firstName; } + public String getLastName() { return lastName; } + public List getProsecutionCasesOrApplications() { return prosecutionCasesOrApplications; } + public List getResults() { return results; } + + public static Builder informantRegisterDefendant() { + return new Builder(); + } + + public static class Builder { + private String name; + private String address1; + private String address2; + private String address3; + private String address4; + private String address5; + private String postCode; + private String dateOfBirth; + private String nationality; + private String title; + private String firstName; + private String lastName; + private List prosecutionCasesOrApplications; + private List results; + + public Builder withName(final String name) { this.name = name; return this; } + public Builder withAddress1(final String address1) { this.address1 = address1; return this; } + public Builder withAddress2(final String address2) { this.address2 = address2; return this; } + public Builder withAddress3(final String address3) { this.address3 = address3; return this; } + public Builder withAddress4(final String address4) { this.address4 = address4; return this; } + public Builder withAddress5(final String address5) { this.address5 = address5; return this; } + public Builder withPostCode(final String postCode) { this.postCode = postCode; return this; } + public Builder withDateOfBirth(final String dateOfBirth) { this.dateOfBirth = dateOfBirth; return this; } + public Builder withNationality(final String nationality) { this.nationality = nationality; return this; } + public Builder withTitle(final String title) { this.title = title; return this; } + public Builder withFirstName(final String firstName) { this.firstName = firstName; return this; } + public Builder withLastName(final String lastName) { this.lastName = lastName; return this; } + public Builder withProsecutionCasesOrApplications(final List prosecutionCasesOrApplications) { + this.prosecutionCasesOrApplications = prosecutionCasesOrApplications; return this; + } + public Builder withResults(final List results) { this.results = results; return this; } + + public InformantRegisterDefendant build() { + return new InformantRegisterDefendant(name, address1, address2, address3, address4, address5, + postCode, dateOfBirth, nationality, title, firstName, lastName, + prosecutionCasesOrApplications, results); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequest.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequest.java new file mode 100644 index 000000000..47b673825 --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequest.java @@ -0,0 +1,103 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import java.time.ZonedDateTime; +import java.util.List; +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class InformantRegisterDocumentRequest { + + private final ZonedDateTime registerDate; + private final ZonedDateTime hearingDate; + private final UUID hearingId; + private final UUID prosecutionAuthorityId; + private final String prosecutionAuthorityCode; + private final String prosecutionAuthorityOuCode; + private final String majorCreditorCode; + private final String prosecutionAuthorityName; + private final String fileName; + private final List recipients; + private final InformantRegisterHearingVenue hearingVenue; + private final UUID groupId; + + @JsonCreator + public InformantRegisterDocumentRequest( + @JsonProperty("registerDate") final ZonedDateTime registerDate, + @JsonProperty("hearingDate") final ZonedDateTime hearingDate, + @JsonProperty("hearingId") final UUID hearingId, + @JsonProperty("prosecutionAuthorityId") final UUID prosecutionAuthorityId, + @JsonProperty("prosecutionAuthorityCode") final String prosecutionAuthorityCode, + @JsonProperty("prosecutionAuthorityOuCode") final String prosecutionAuthorityOuCode, + @JsonProperty("majorCreditorCode") final String majorCreditorCode, + @JsonProperty("prosecutionAuthorityName") final String prosecutionAuthorityName, + @JsonProperty("fileName") final String fileName, + @JsonProperty("recipients") final List recipients, + @JsonProperty("hearingVenue") final InformantRegisterHearingVenue hearingVenue, + @JsonProperty("groupId") final UUID groupId) { + this.registerDate = registerDate; + this.hearingDate = hearingDate; + this.hearingId = hearingId; + this.prosecutionAuthorityId = prosecutionAuthorityId; + this.prosecutionAuthorityCode = prosecutionAuthorityCode; + this.prosecutionAuthorityOuCode = prosecutionAuthorityOuCode; + this.majorCreditorCode = majorCreditorCode; + this.prosecutionAuthorityName = prosecutionAuthorityName; + this.fileName = fileName; + this.recipients = recipients; + this.hearingVenue = hearingVenue; + this.groupId = groupId; + } + + public ZonedDateTime getRegisterDate() { return registerDate; } + public ZonedDateTime getHearingDate() { return hearingDate; } + public UUID getHearingId() { return hearingId; } + public UUID getProsecutionAuthorityId() { return prosecutionAuthorityId; } + public String getProsecutionAuthorityCode() { return prosecutionAuthorityCode; } + public String getProsecutionAuthorityOuCode() { return prosecutionAuthorityOuCode; } + public String getMajorCreditorCode() { return majorCreditorCode; } + public String getProsecutionAuthorityName() { return prosecutionAuthorityName; } + public String getFileName() { return fileName; } + public List getRecipients() { return recipients; } + public InformantRegisterHearingVenue getHearingVenue() { return hearingVenue; } + public UUID getGroupId() { return groupId; } + + public static Builder informantRegisterDocumentRequest() { + return new Builder(); + } + + public static class Builder { + private ZonedDateTime registerDate; + private ZonedDateTime hearingDate; + private UUID hearingId; + private UUID prosecutionAuthorityId; + private String prosecutionAuthorityCode; + private String prosecutionAuthorityOuCode; + private String majorCreditorCode; + private String prosecutionAuthorityName; + private String fileName; + private List recipients; + private InformantRegisterHearingVenue hearingVenue; + private UUID groupId; + + public Builder withRegisterDate(final ZonedDateTime registerDate) { this.registerDate = registerDate; return this; } + public Builder withHearingDate(final ZonedDateTime hearingDate) { this.hearingDate = hearingDate; return this; } + public Builder withHearingId(final UUID hearingId) { this.hearingId = hearingId; return this; } + public Builder withProsecutionAuthorityId(final UUID prosecutionAuthorityId) { this.prosecutionAuthorityId = prosecutionAuthorityId; return this; } + public Builder withProsecutionAuthorityCode(final String prosecutionAuthorityCode) { this.prosecutionAuthorityCode = prosecutionAuthorityCode; return this; } + public Builder withProsecutionAuthorityOuCode(final String prosecutionAuthorityOuCode) { this.prosecutionAuthorityOuCode = prosecutionAuthorityOuCode; return this; } + public Builder withMajorCreditorCode(final String majorCreditorCode) { this.majorCreditorCode = majorCreditorCode; return this; } + public Builder withProsecutionAuthorityName(final String prosecutionAuthorityName) { this.prosecutionAuthorityName = prosecutionAuthorityName; return this; } + public Builder withFileName(final String fileName) { this.fileName = fileName; return this; } + public Builder withRecipients(final List recipients) { this.recipients = recipients; return this; } + public Builder withHearingVenue(final InformantRegisterHearingVenue hearingVenue) { this.hearingVenue = hearingVenue; return this; } + public Builder withGroupId(final UUID groupId) { this.groupId = groupId; return this; } + + public InformantRegisterDocumentRequest build() { + return new InformantRegisterDocumentRequest(registerDate, hearingDate, hearingId, + prosecutionAuthorityId, prosecutionAuthorityCode, prosecutionAuthorityOuCode, + majorCreditorCode, prosecutionAuthorityName, fileName, recipients, hearingVenue, groupId); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearing.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearing.java new file mode 100644 index 000000000..0ead3ec24 --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearing.java @@ -0,0 +1,64 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class InformantRegisterHearing { + + private final String courtRoom; + private final String hearingStartTime; + private final List defendants; + + @JsonCreator + public InformantRegisterHearing( + @JsonProperty("courtRoom") final String courtRoom, + @JsonProperty("hearingStartTime") final String hearingStartTime, + @JsonProperty("defendants") final List defendants) { + this.courtRoom = courtRoom; + this.hearingStartTime = hearingStartTime; + this.defendants = defendants; + } + + public String getCourtRoom() { + return courtRoom; + } + + public String getHearingStartTime() { + return hearingStartTime; + } + + public List getDefendants() { + return defendants; + } + + public static Builder informantRegisterHearing() { + return new Builder(); + } + + public static class Builder { + private String courtRoom; + private String hearingStartTime; + private List defendants; + + public Builder withCourtRoom(final String courtRoom) { + this.courtRoom = courtRoom; + return this; + } + + public Builder withHearingStartTime(final String hearingStartTime) { + this.hearingStartTime = hearingStartTime; + return this; + } + + public Builder withDefendants(final List defendants) { + this.defendants = defendants; + return this; + } + + public InformantRegisterHearing build() { + return new InformantRegisterHearing(courtRoom, hearingStartTime, defendants); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenue.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenue.java new file mode 100644 index 000000000..b0adf9327 --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenue.java @@ -0,0 +1,64 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class InformantRegisterHearingVenue { + + private final String ljaName; + private final String courtHouse; + private final List courtSessions; + + @JsonCreator + public InformantRegisterHearingVenue( + @JsonProperty("ljaName") final String ljaName, + @JsonProperty("courtHouse") final String courtHouse, + @JsonProperty("courtSessions") final List courtSessions) { + this.ljaName = ljaName; + this.courtHouse = courtHouse; + this.courtSessions = courtSessions; + } + + public String getLjaName() { + return ljaName; + } + + public String getCourtHouse() { + return courtHouse; + } + + public List getCourtSessions() { + return courtSessions; + } + + public static Builder informantRegisterHearingVenue() { + return new Builder(); + } + + public static class Builder { + private String ljaName; + private String courtHouse; + private List courtSessions; + + public Builder withLjaName(final String ljaName) { + this.ljaName = ljaName; + return this; + } + + public Builder withCourtHouse(final String courtHouse) { + this.courtHouse = courtHouse; + return this; + } + + public Builder withCourtSessions(final List courtSessions) { + this.courtSessions = courtSessions; + return this; + } + + public InformantRegisterHearingVenue build() { + return new InformantRegisterHearingVenue(ljaName, courtHouse, courtSessions); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffence.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffence.java new file mode 100644 index 000000000..845877249 --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffence.java @@ -0,0 +1,119 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class InformantRegisterOffence { + + private final String offenceCode; + private final String offenceTitle; + private final Integer orderIndex; + private final String originatingCaseUrn; + private final String pleaValue; + @JsonInclude(JsonInclude.Include.NON_NULL) + private final Verdict verdict; + private final List offenceResults; + + @JsonCreator + public InformantRegisterOffence( + @JsonProperty("offenceCode") final String offenceCode, + @JsonProperty("offenceTitle") final String offenceTitle, + @JsonProperty("orderIndex") final Integer orderIndex, + @JsonProperty("originatingCaseUrn") final String originatingCaseUrn, + @JsonProperty("pleaValue") final String pleaValue, + @JsonProperty("verdict") final Verdict verdict, + @JsonProperty("offenceResults") final List offenceResults) { + this.offenceCode = offenceCode; + this.offenceTitle = offenceTitle; + this.orderIndex = orderIndex; + this.originatingCaseUrn = originatingCaseUrn; + this.pleaValue = pleaValue; + this.verdict = verdict; + this.offenceResults = offenceResults; + } + + public String getOffenceCode() { + return offenceCode; + } + + public String getOffenceTitle() { + return offenceTitle; + } + + public Integer getOrderIndex() { + return orderIndex; + } + + public String getOriginatingCaseUrn() { + return originatingCaseUrn; + } + + public String getPleaValue() { + return pleaValue; + } + + public Verdict getVerdict() { + return verdict; + } + + public List getOffenceResults() { + return offenceResults; + } + + public static Builder informantRegisterOffence() { + return new Builder(); + } + + public static class Builder { + private String offenceCode; + private String offenceTitle; + private Integer orderIndex; + private String originatingCaseUrn; + private String pleaValue; + private Verdict verdict; + private List offenceResults; + + public Builder withOffenceCode(final String offenceCode) { + this.offenceCode = offenceCode; + return this; + } + + public Builder withOffenceTitle(final String offenceTitle) { + this.offenceTitle = offenceTitle; + return this; + } + + public Builder withOrderIndex(final Integer orderIndex) { + this.orderIndex = orderIndex; + return this; + } + + public Builder withOriginatingCaseUrn(final String originatingCaseUrn) { + this.originatingCaseUrn = originatingCaseUrn; + return this; + } + + public Builder withPleaValue(final String pleaValue) { + this.pleaValue = pleaValue; + return this; + } + + public Builder withVerdict(final Verdict verdict) { + this.verdict = verdict; + return this; + } + + public Builder withOffenceResults(final List offenceResults) { + this.offenceResults = offenceResults; + return this; + } + + public InformantRegisterOffence build() { + return new InformantRegisterOffence(offenceCode, offenceTitle, orderIndex, + originatingCaseUrn, pleaValue, verdict, offenceResults); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipient.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipient.java new file mode 100644 index 000000000..fd31d7c40 --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipient.java @@ -0,0 +1,79 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; + +public class InformantRegisterRecipient implements Serializable { + + private static final long serialVersionUID = 1L; + + private final String recipientName; + private final String emailAddress1; + private final String emailAddress2; + private final String emailTemplateName; + + @JsonCreator + public InformantRegisterRecipient( + @JsonProperty("recipientName") final String recipientName, + @JsonProperty("emailAddress1") final String emailAddress1, + @JsonProperty("emailAddress2") final String emailAddress2, + @JsonProperty("emailTemplateName") final String emailTemplateName) { + this.recipientName = recipientName; + this.emailAddress1 = emailAddress1; + this.emailAddress2 = emailAddress2; + this.emailTemplateName = emailTemplateName; + } + + public String getRecipientName() { + return recipientName; + } + + public String getEmailAddress1() { + return emailAddress1; + } + + public String getEmailAddress2() { + return emailAddress2; + } + + public String getEmailTemplateName() { + return emailTemplateName; + } + + public static Builder informantRegisterRecipient() { + return new Builder(); + } + + public static class Builder { + private String recipientName; + private String emailAddress1; + private String emailAddress2; + private String emailTemplateName; + + public Builder withRecipientName(final String recipientName) { + this.recipientName = recipientName; + return this; + } + + public Builder withEmailAddress1(final String emailAddress1) { + this.emailAddress1 = emailAddress1; + return this; + } + + public Builder withEmailAddress2(final String emailAddress2) { + this.emailAddress2 = emailAddress2; + return this; + } + + public Builder withEmailTemplateName(final String emailTemplateName) { + this.emailTemplateName = emailTemplateName; + return this; + } + + public InformantRegisterRecipient build() { + return new InformantRegisterRecipient(recipientName, emailAddress1, emailAddress2, emailTemplateName); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResult.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResult.java new file mode 100644 index 000000000..dbb738e92 --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResult.java @@ -0,0 +1,62 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class InformantRegisterResult { + + private final String resultText; + private final String cjsResultCode; + private final InformantRegisterResultData resultData; + + @JsonCreator + public InformantRegisterResult( + @JsonProperty("resultText") final String resultText, + @JsonProperty("cjsResultCode") final String cjsResultCode, + @JsonProperty("resultData") final InformantRegisterResultData resultData) { + this.resultText = resultText; + this.cjsResultCode = cjsResultCode; + this.resultData = resultData; + } + + public String getResultText() { + return resultText; + } + + public String getCjsResultCode() { + return cjsResultCode; + } + + public InformantRegisterResultData getResultData() { + return resultData; + } + + public static Builder informantRegisterResult() { + return new Builder(); + } + + public static class Builder { + private String resultText; + private String cjsResultCode; + private InformantRegisterResultData resultData; + + public Builder withResultText(final String resultText) { + this.resultText = resultText; + return this; + } + + public Builder withCjsResultCode(final String cjsResultCode) { + this.cjsResultCode = cjsResultCode; + return this; + } + + public Builder withResultData(final InformantRegisterResultData resultData) { + this.resultData = resultData; + return this; + } + + public InformantRegisterResult build() { + return new InformantRegisterResult(resultText, cjsResultCode, resultData); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultData.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultData.java new file mode 100644 index 000000000..d638a694b --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultData.java @@ -0,0 +1,142 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class InformantRegisterResultData { + + private final String amount; + private final String nextHearingDate; + private final String nextCourtLocation; + private final String durationValue; + private final String durationUnit; + private final String durationStartDate; + private final String durationEndDate; + private final String secondaryDurationValue; + private final String secondaryDurationUnit; + + @JsonCreator + public InformantRegisterResultData( + @JsonProperty("amount") final String amount, + @JsonProperty("nextHearingDate") final String nextHearingDate, + @JsonProperty("nextCourtLocation") final String nextCourtLocation, + @JsonProperty("durationValue") final String durationValue, + @JsonProperty("durationUnit") final String durationUnit, + @JsonProperty("durationStartDate") final String durationStartDate, + @JsonProperty("durationEndDate") final String durationEndDate, + @JsonProperty("secondaryDurationValue") final String secondaryDurationValue, + @JsonProperty("secondaryDurationUnit") final String secondaryDurationUnit) { + this.amount = amount; + this.nextHearingDate = nextHearingDate; + this.nextCourtLocation = nextCourtLocation; + this.durationValue = durationValue; + this.durationUnit = durationUnit; + this.durationStartDate = durationStartDate; + this.durationEndDate = durationEndDate; + this.secondaryDurationValue = secondaryDurationValue; + this.secondaryDurationUnit = secondaryDurationUnit; + } + + public String getAmount() { + return amount; + } + + public String getNextHearingDate() { + return nextHearingDate; + } + + public String getNextCourtLocation() { + return nextCourtLocation; + } + + public String getDurationValue() { + return durationValue; + } + + public String getDurationUnit() { + return durationUnit; + } + + public String getDurationStartDate() { + return durationStartDate; + } + + public String getDurationEndDate() { + return durationEndDate; + } + + public String getSecondaryDurationValue() { + return secondaryDurationValue; + } + + public String getSecondaryDurationUnit() { + return secondaryDurationUnit; + } + + public static Builder informantRegisterResultData() { + return new Builder(); + } + + public static class Builder { + private String amount; + private String nextHearingDate; + private String nextCourtLocation; + private String durationValue; + private String durationUnit; + private String durationStartDate; + private String durationEndDate; + private String secondaryDurationValue; + private String secondaryDurationUnit; + + public Builder withAmount(final String amount) { + this.amount = amount; + return this; + } + + public Builder withNextHearingDate(final String nextHearingDate) { + this.nextHearingDate = nextHearingDate; + return this; + } + + public Builder withNextCourtLocation(final String nextCourtLocation) { + this.nextCourtLocation = nextCourtLocation; + return this; + } + + public Builder withDurationValue(final String durationValue) { + this.durationValue = durationValue; + return this; + } + + public Builder withDurationUnit(final String durationUnit) { + this.durationUnit = durationUnit; + return this; + } + + public Builder withDurationStartDate(final String durationStartDate) { + this.durationStartDate = durationStartDate; + return this; + } + + public Builder withDurationEndDate(final String durationEndDate) { + this.durationEndDate = durationEndDate; + return this; + } + + public Builder withSecondaryDurationValue(final String secondaryDurationValue) { + this.secondaryDurationValue = secondaryDurationValue; + return this; + } + + public Builder withSecondaryDurationUnit(final String secondaryDurationUnit) { + this.secondaryDurationUnit = secondaryDurationUnit; + return this; + } + + public InformantRegisterResultData build() { + return new InformantRegisterResultData(amount, nextHearingDate, nextCourtLocation, + durationValue, durationUnit, durationStartDate, durationEndDate, + secondaryDurationValue, secondaryDurationUnit); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResult.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResult.java new file mode 100644 index 000000000..56d1682db --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResult.java @@ -0,0 +1,79 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import java.time.LocalDate; +import java.util.List; +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ProsecutorResult { + + private final LocalDate startDate; + private final LocalDate endDate; + private final UUID prosecutionAuthorityId; + private final String prosecutionAuthorityCode; + private final String prosecutionAuthorityName; + private final String prosecutionAuthorityOuCode; + private final String majorCreditorCode; + private final List hearingVenues; + + @JsonCreator + public ProsecutorResult( + @JsonProperty("startDate") final LocalDate startDate, + @JsonProperty("endDate") final LocalDate endDate, + @JsonProperty("prosecutionAuthorityId") final UUID prosecutionAuthorityId, + @JsonProperty("prosecutionAuthorityCode") final String prosecutionAuthorityCode, + @JsonProperty("prosecutionAuthorityName") final String prosecutionAuthorityName, + @JsonProperty("prosecutionAuthorityOuCode") final String prosecutionAuthorityOuCode, + @JsonProperty("majorCreditorCode") final String majorCreditorCode, + @JsonProperty("hearingVenues") final List hearingVenues) { + this.startDate = startDate; + this.endDate = endDate; + this.prosecutionAuthorityId = prosecutionAuthorityId; + this.prosecutionAuthorityCode = prosecutionAuthorityCode; + this.prosecutionAuthorityName = prosecutionAuthorityName; + this.prosecutionAuthorityOuCode = prosecutionAuthorityOuCode; + this.majorCreditorCode = majorCreditorCode; + this.hearingVenues = hearingVenues; + } + + public LocalDate getStartDate() { return startDate; } + public LocalDate getEndDate() { return endDate; } + public UUID getProsecutionAuthorityId() { return prosecutionAuthorityId; } + public String getProsecutionAuthorityCode() { return prosecutionAuthorityCode; } + public String getProsecutionAuthorityName() { return prosecutionAuthorityName; } + public String getProsecutionAuthorityOuCode() { return prosecutionAuthorityOuCode; } + public String getMajorCreditorCode() { return majorCreditorCode; } + public List getHearingVenues() { return hearingVenues; } + + public static Builder prosecutorResult() { + return new Builder(); + } + + public static class Builder { + private LocalDate startDate; + private LocalDate endDate; + private UUID prosecutionAuthorityId; + private String prosecutionAuthorityCode; + private String prosecutionAuthorityName; + private String prosecutionAuthorityOuCode; + private String majorCreditorCode; + private List hearingVenues; + + public Builder withStartDate(final LocalDate startDate) { this.startDate = startDate; return this; } + public Builder withEndDate(final LocalDate endDate) { this.endDate = endDate; return this; } + public Builder withProsecutionAuthorityId(final UUID prosecutionAuthorityId) { this.prosecutionAuthorityId = prosecutionAuthorityId; return this; } + public Builder withProsecutionAuthorityCode(final String prosecutionAuthorityCode) { this.prosecutionAuthorityCode = prosecutionAuthorityCode; return this; } + public Builder withProsecutionAuthorityName(final String prosecutionAuthorityName) { this.prosecutionAuthorityName = prosecutionAuthorityName; return this; } + public Builder withProsecutionAuthorityOuCode(final String prosecutionAuthorityOuCode) { this.prosecutionAuthorityOuCode = prosecutionAuthorityOuCode; return this; } + public Builder withMajorCreditorCode(final String majorCreditorCode) { this.majorCreditorCode = majorCreditorCode; return this; } + public Builder withHearingVenues(final List hearingVenues) { this.hearingVenues = hearingVenues; return this; } + + public ProsecutorResult build() { + return new ProsecutorResult(startDate, endDate, prosecutionAuthorityId, + prosecutionAuthorityCode, prosecutionAuthorityName, prosecutionAuthorityOuCode, + majorCreditorCode, hearingVenues); + } + } +} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/Verdict.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/Verdict.java new file mode 100644 index 000000000..48ce12113 --- /dev/null +++ b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/Verdict.java @@ -0,0 +1,62 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Verdict { + + private final String verdictCode; + private final String verdictDate; + private final String verdictType; + + @JsonCreator + public Verdict( + @JsonProperty("verdictCode") final String verdictCode, + @JsonProperty("verdictDate") final String verdictDate, + @JsonProperty("verdictType") final String verdictType) { + this.verdictCode = verdictCode; + this.verdictDate = verdictDate; + this.verdictType = verdictType; + } + + public String getVerdictCode() { + return verdictCode; + } + + public String getVerdictDate() { + return verdictDate; + } + + public String getVerdictType() { + return verdictType; + } + + public static Builder verdict() { + return new Builder(); + } + + public static class Builder { + private String verdictCode; + private String verdictDate; + private String verdictType; + + public Builder withVerdictCode(final String verdictCode) { + this.verdictCode = verdictCode; + return this; + } + + public Builder withVerdictDate(final String verdictDate) { + this.verdictDate = verdictDate; + return this; + } + + public Builder withVerdictType(final String verdictType) { + this.verdictType = verdictType; + return this; + } + + public Verdict build() { + return new Verdict(verdictCode, verdictDate, verdictType); + } + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2Test.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2Test.java new file mode 100644 index 000000000..4adf9a86f --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2Test.java @@ -0,0 +1,61 @@ +package uk.gov.moj.cpp.results.domain.event; + +import static java.util.Collections.singletonList; +import static java.util.UUID.randomUUID; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; + +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue; + +import java.time.ZonedDateTime; +import java.util.List; + +import org.junit.jupiter.api.Test; + +public class InformantRegisterGeneratedV2Test { + + @Test + public void name_constantEqualsEventName() { + assertThat(InformantRegisterGeneratedV2.NAME, is("results.event.informant-register-generated-v2")); + } + + @Test + public void builder_setsDocumentRequestsList() { + final InformantRegisterDocumentRequest request = InformantRegisterDocumentRequest.informantRegisterDocumentRequest() + .withRegisterDate(ZonedDateTime.parse("2026-04-01T09:00:00Z")) + .withHearingId(randomUUID()) + .withProsecutionAuthorityId(randomUUID()) + .withProsecutionAuthorityCode("TFL") + .withHearingVenue(InformantRegisterHearingVenue.informantRegisterHearingVenue() + .withCourtHouse("Crown Court").build()) + .build(); + + final InformantRegisterGeneratedV2 event = InformantRegisterGeneratedV2.informantRegisterGeneratedV2() + .withInformantRegisterDocumentRequests(singletonList(request)) + .withSystemGenerated(true) + .build(); + + final List requests = event.getInformantRegisterDocumentRequests(); + assertThat(requests, hasSize(1)); + assertThat(event.getSystemGenerated(), is(true)); + } + + @Test + public void builder_systemGeneratedOptional() { + final InformantRegisterGeneratedV2 event = InformantRegisterGeneratedV2.informantRegisterGeneratedV2() + .withInformantRegisterDocumentRequests(singletonList( + InformantRegisterDocumentRequest.informantRegisterDocumentRequest() + .withRegisterDate(ZonedDateTime.parse("2026-04-01T09:00:00Z")) + .withHearingId(randomUUID()) + .withProsecutionAuthorityId(randomUUID()) + .withProsecutionAuthorityCode("TFL") + .withHearingVenue(InformantRegisterHearingVenue.informantRegisterHearingVenue() + .withCourtHouse("Crown Court").build()) + .build())) + .build(); + + assertThat(event.getSystemGenerated(), is((Boolean) null)); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2Test.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2Test.java new file mode 100644 index 000000000..acaec639d --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2Test.java @@ -0,0 +1,43 @@ +package uk.gov.moj.cpp.results.domain.event; + +import static java.util.UUID.randomUUID; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue; + +import java.time.ZonedDateTime; +import java.util.UUID; + +import org.junit.jupiter.api.Test; + +public class InformantRegisterRecordedV2Test { + + @Test + public void name_constantEqualsEventName() { + assertThat(InformantRegisterRecordedV2.NAME, is("results.event.informant-register-recorded-v2")); + } + + @Test + public void builder_setsProsecutionAuthorityId() { + final UUID prosecutionAuthorityId = randomUUID(); + final InformantRegisterDocumentRequest request = InformantRegisterDocumentRequest.informantRegisterDocumentRequest() + .withRegisterDate(ZonedDateTime.parse("2026-04-01T09:00:00Z")) + .withHearingId(randomUUID()) + .withProsecutionAuthorityId(prosecutionAuthorityId) + .withProsecutionAuthorityCode("TFL") + .withHearingVenue(InformantRegisterHearingVenue.informantRegisterHearingVenue() + .withCourtHouse("Crown Court").build()) + .build(); + + final InformantRegisterRecordedV2 event = InformantRegisterRecordedV2.informantRegisterRecordedV2() + .withProsecutionAuthorityId(prosecutionAuthorityId) + .withInformantRegister(request) + .build(); + + assertThat(event.getProsecutionAuthorityId(), is(prosecutionAuthorityId)); + assertThat(event.getInformantRegister(), is(notNullValue())); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplicationTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplicationTest.java new file mode 100644 index 000000000..db4d01efd --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplicationTest.java @@ -0,0 +1,44 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static java.util.Collections.singletonList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.junit.jupiter.api.Test; + +public class InformantRegisterCaseOrApplicationTest { + + @Test + public void builder_setsAllFields() { + final InformantRegisterOffence offence = InformantRegisterOffence.informantRegisterOffence() + .withOffenceCode("AB001").withOffenceTitle("Theft").withOrderIndex(1).build(); + + final InformantRegisterCaseOrApplication coa = InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication() + .withCaseOrApplicationReference("URN-001") + .withArrestSummonsNumber("ASN-001") + .withApplicationParticulars("particulars") + .withOffences(singletonList(offence)) + .build(); + + assertThat(coa.getCaseOrApplicationReference(), is("URN-001")); + assertThat(coa.getArrestSummonsNumber(), is("ASN-001")); + assertThat(coa.getApplicationParticulars(), is("particulars")); + assertThat(coa.getOffences(), hasSize(1)); + assertThat(coa.getOffences().get(0).getOffenceCode(), is("AB001")); + } + + @Test + public void jacksonDeserialization_roundtrip() throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final String json = "{\"caseOrApplicationReference\":\"URN-001\",\"offences\":[" + + "{\"offenceCode\":\"AB001\",\"offenceTitle\":\"Theft\",\"orderIndex\":1}]}"; + + final InformantRegisterCaseOrApplication coa = mapper.readValue(json, InformantRegisterCaseOrApplication.class); + + assertThat(coa.getCaseOrApplicationReference(), is("URN-001")); + assertThat(coa.getOffences(), hasSize(1)); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendantTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendantTest.java new file mode 100644 index 000000000..3b67e05d7 --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendantTest.java @@ -0,0 +1,63 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static java.util.Collections.singletonList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.junit.jupiter.api.Test; + +public class InformantRegisterDefendantTest { + + @Test + public void builder_setsRequiredFields() { + final InformantRegisterDefendant defendant = InformantRegisterDefendant.informantRegisterDefendant() + .withName("John Smith") + .withAddress1("1 High Street") + .withFirstName("John") + .withLastName("Smith") + .build(); + + assertThat(defendant.getName(), is("John Smith")); + assertThat(defendant.getAddress1(), is("1 High Street")); + assertThat(defendant.getFirstName(), is("John")); + assertThat(defendant.getLastName(), is("Smith")); + } + + @Test + public void builder_optionalFieldsAreNull() { + final InformantRegisterDefendant defendant = InformantRegisterDefendant.informantRegisterDefendant() + .withName("John Smith").withAddress1("1 High Street").build(); + + assertThat(defendant.getAddress2(), is(nullValue())); + assertThat(defendant.getDateOfBirth(), is(nullValue())); + assertThat(defendant.getNationality(), is(nullValue())); + } + + @Test + public void builder_withCasesOrApplications_setsCorrectly() { + final InformantRegisterCaseOrApplication coa = InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication() + .withCaseOrApplicationReference("URN-001").build(); + + final InformantRegisterDefendant defendant = InformantRegisterDefendant.informantRegisterDefendant() + .withName("John").withAddress1("1 Street") + .withProsecutionCasesOrApplications(singletonList(coa)) + .build(); + + assertThat(defendant.getProsecutionCasesOrApplications(), hasSize(1)); + } + + @Test + public void jacksonDeserialization_roundtrip() throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final String json = "{\"name\":\"John Smith\",\"address1\":\"1 High Street\",\"firstName\":\"John\",\"lastName\":\"Smith\"}"; + + final InformantRegisterDefendant defendant = mapper.readValue(json, InformantRegisterDefendant.class); + + assertThat(defendant.getName(), is("John Smith")); + assertThat(defendant.getFirstName(), is("John")); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequestTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequestTest.java new file mode 100644 index 000000000..4838e2813 --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequestTest.java @@ -0,0 +1,75 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static java.util.Collections.singletonList; +import static java.util.UUID.randomUUID; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +import java.time.ZonedDateTime; +import java.util.UUID; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import org.junit.jupiter.api.Test; + +public class InformantRegisterDocumentRequestTest { + + @Test + public void builder_setsAllRequiredFields() { + final UUID hearingId = randomUUID(); + final UUID prosecutionAuthorityId = randomUUID(); + final ZonedDateTime registerDate = ZonedDateTime.parse("2026-04-13T09:00:00Z"); + + final InformantRegisterHearingVenue venue = InformantRegisterHearingVenue.informantRegisterHearingVenue() + .withCourtHouse("Crown Court").build(); + + final InformantRegisterDocumentRequest request = InformantRegisterDocumentRequest.informantRegisterDocumentRequest() + .withRegisterDate(registerDate) + .withHearingDate(registerDate) + .withHearingId(hearingId) + .withProsecutionAuthorityId(prosecutionAuthorityId) + .withProsecutionAuthorityCode("TFL") + .withFileName("file.csv") + .withHearingVenue(venue) + .build(); + + assertThat(request.getRegisterDate(), is(registerDate)); + assertThat(request.getHearingId(), is(hearingId)); + assertThat(request.getProsecutionAuthorityId(), is(prosecutionAuthorityId)); + assertThat(request.getProsecutionAuthorityCode(), is("TFL")); + assertThat(request.getFileName(), is("file.csv")); + assertThat(request.getHearingVenue(), is(notNullValue())); + } + + @Test + public void jacksonDeserialization_nestedVerdictGraph_preservesVerdictCode() throws Exception { + final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()); + final String json = "{" + + "\"registerDate\":\"2026-04-13T09:00:00Z\"," + + "\"hearingDate\":\"2026-04-13T09:00:00Z\"," + + "\"hearingId\":\"" + randomUUID() + "\"," + + "\"prosecutionAuthorityId\":\"" + randomUUID() + "\"," + + "\"prosecutionAuthorityCode\":\"TFL\"," + + "\"fileName\":\"file.csv\"," + + "\"hearingVenue\":{\"courtHouse\":\"Crown Court\",\"courtSessions\":[{" + + "\"courtRoom\":\"Room 1\",\"hearingStartTime\":\"09:00\"," + + "\"defendants\":[{\"name\":\"John\",\"address1\":\"1 Street\"," + + "\"prosecutionCasesOrApplications\":[{" + + "\"caseOrApplicationReference\":\"URN-001\"," + + "\"offences\":[{\"offenceCode\":\"AB001\",\"offenceTitle\":\"Theft\",\"orderIndex\":1," + + "\"verdict\":{\"verdictCode\":\"G\",\"verdictDate\":\"2026-04-13\"}}]}]}]}]}}"; + + final InformantRegisterDocumentRequest request = mapper.readValue(json, InformantRegisterDocumentRequest.class); + + assertThat(request.getHearingVenue(), is(notNullValue())); + final String verdictCode = request.getHearingVenue() + .getCourtSessions().get(0) + .getDefendants().get(0) + .getProsecutionCasesOrApplications().get(0) + .getOffences().get(0) + .getVerdict().getVerdictCode(); + assertThat(verdictCode, is("G")); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingTest.java new file mode 100644 index 000000000..a4af837ba --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingTest.java @@ -0,0 +1,41 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static java.util.Collections.singletonList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.junit.jupiter.api.Test; + +public class InformantRegisterHearingTest { + + @Test + public void builder_setsAllFields() { + final InformantRegisterDefendant defendant = InformantRegisterDefendant.informantRegisterDefendant() + .withName("John").withAddress1("1 Street").build(); + + final InformantRegisterHearing hearing = InformantRegisterHearing.informantRegisterHearing() + .withCourtRoom("Room 1") + .withHearingStartTime("09:00") + .withDefendants(singletonList(defendant)) + .build(); + + assertThat(hearing.getCourtRoom(), is("Room 1")); + assertThat(hearing.getHearingStartTime(), is("09:00")); + assertThat(hearing.getDefendants(), hasSize(1)); + } + + @Test + public void jacksonDeserialization_roundtrip() throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final String json = "{\"courtRoom\":\"Room 1\",\"hearingStartTime\":\"09:00\",\"defendants\":[" + + "{\"name\":\"John\",\"address1\":\"1 Street\"}]}"; + + final InformantRegisterHearing hearing = mapper.readValue(json, InformantRegisterHearing.class); + + assertThat(hearing.getCourtRoom(), is("Room 1")); + assertThat(hearing.getDefendants(), hasSize(1)); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenueTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenueTest.java new file mode 100644 index 000000000..d71792cf9 --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenueTest.java @@ -0,0 +1,50 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static java.util.Collections.singletonList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.junit.jupiter.api.Test; + +public class InformantRegisterHearingVenueTest { + + @Test + public void builder_setsRequiredFields() { + final InformantRegisterHearing hearing = InformantRegisterHearing.informantRegisterHearing() + .withCourtRoom("Room 1").withHearingStartTime("09:00").build(); + + final InformantRegisterHearingVenue venue = InformantRegisterHearingVenue.informantRegisterHearingVenue() + .withCourtHouse("Crown Court") + .withLjaName("LJA1") + .withCourtSessions(singletonList(hearing)) + .build(); + + assertThat(venue.getCourtHouse(), is("Crown Court")); + assertThat(venue.getLjaName(), is("LJA1")); + assertThat(venue.getCourtSessions(), hasSize(1)); + } + + @Test + public void builder_ljaNameOptional() { + final InformantRegisterHearingVenue venue = InformantRegisterHearingVenue.informantRegisterHearingVenue() + .withCourtHouse("Crown Court").build(); + + assertThat(venue.getLjaName(), is(nullValue())); + } + + @Test + public void jacksonDeserialization_roundtrip() throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final String json = "{\"courtHouse\":\"Crown Court\",\"courtSessions\":[" + + "{\"courtRoom\":\"Room 1\",\"hearingStartTime\":\"09:00\",\"defendants\":[]}]}"; + + final InformantRegisterHearingVenue venue = mapper.readValue(json, InformantRegisterHearingVenue.class); + + assertThat(venue.getCourtHouse(), is("Crown Court")); + assertThat(venue.getCourtSessions(), hasSize(1)); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffenceTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffenceTest.java new file mode 100644 index 000000000..745aa3706 --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffenceTest.java @@ -0,0 +1,66 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.junit.jupiter.api.Test; + +public class InformantRegisterOffenceTest { + + @Test + public void builder_setsVerdictField() { + final Verdict verdict = Verdict.verdict().withVerdictCode("G").withVerdictDate("2026-04-13").build(); + + final InformantRegisterOffence offence = InformantRegisterOffence.informantRegisterOffence() + .withOffenceCode("AB001") + .withOffenceTitle("Theft") + .withOrderIndex(1) + .withVerdict(verdict) + .build(); + + assertThat(offence.getVerdict(), is(notNullValue())); + assertThat(offence.getVerdict().getVerdictCode(), is("G")); + assertThat(offence.getVerdict().getVerdictDate(), is("2026-04-13")); + assertThat(offence.getOffenceCode(), is("AB001")); + assertThat(offence.getOffenceTitle(), is("Theft")); + assertThat(offence.getOrderIndex(), is(1)); + } + + @Test + public void builder_withoutVerdict_verdictIsNull() { + final InformantRegisterOffence offence = InformantRegisterOffence.informantRegisterOffence() + .withOffenceCode("AB001") + .withOffenceTitle("Theft") + .withOrderIndex(1) + .build(); + + assertThat(offence.getVerdict(), is(nullValue())); + } + + @Test + public void jacksonDeserialization_withVerdictObject_deserializesCorrectly() throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final String json = "{\"offenceCode\":\"AB001\",\"offenceTitle\":\"Theft\",\"orderIndex\":1," + + "\"verdict\":{\"verdictCode\":\"G\",\"verdictDate\":\"2026-04-13\",\"verdictType\":\"FOUND_GUILTY\"}}"; + + final InformantRegisterOffence offence = mapper.readValue(json, InformantRegisterOffence.class); + + assertThat(offence.getOffenceCode(), is("AB001")); + assertThat(offence.getVerdict(), is(notNullValue())); + assertThat(offence.getVerdict().getVerdictCode(), is("G")); + } + + @Test + public void jacksonDeserialization_withNoVerdict_verdictIsNull() throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final String json = "{\"offenceCode\":\"AB001\",\"offenceTitle\":\"Theft\",\"orderIndex\":1}"; + + final InformantRegisterOffence offence = mapper.readValue(json, InformantRegisterOffence.class); + + assertThat(offence.getVerdict(), is(nullValue())); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipientTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipientTest.java new file mode 100644 index 000000000..14f3783e8 --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipientTest.java @@ -0,0 +1,44 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.junit.jupiter.api.Test; + +public class InformantRegisterRecipientTest { + + @Test + public void builder_setsRequiredFields() { + final InformantRegisterRecipient recipient = InformantRegisterRecipient.informantRegisterRecipient() + .withRecipientName("Prosecutor A") + .withEmailAddress1("a@example.com") + .withEmailTemplateName("TEMPLATE_1") + .build(); + + assertThat(recipient.getRecipientName(), is("Prosecutor A")); + assertThat(recipient.getEmailAddress1(), is("a@example.com")); + assertThat(recipient.getEmailTemplateName(), is("TEMPLATE_1")); + } + + @Test + public void builder_emailAddress2IsOptional() { + final InformantRegisterRecipient recipient = InformantRegisterRecipient.informantRegisterRecipient() + .withRecipientName("Prosecutor A").withEmailAddress1("a@example.com").build(); + + assertThat(recipient.getEmailAddress2(), is(nullValue())); + } + + @Test + public void jacksonDeserialization_roundtrip() throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final String json = "{\"recipientName\":\"Prosecutor A\",\"emailAddress1\":\"a@example.com\",\"emailTemplateName\":\"T1\"}"; + + final InformantRegisterRecipient recipient = mapper.readValue(json, InformantRegisterRecipient.class); + + assertThat(recipient.getRecipientName(), is("Prosecutor A")); + assertThat(recipient.getEmailAddress1(), is("a@example.com")); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultDataTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultDataTest.java new file mode 100644 index 000000000..9b59eae16 --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultDataTest.java @@ -0,0 +1,47 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.junit.jupiter.api.Test; + +public class InformantRegisterResultDataTest { + + @Test + public void builder_setsFields() { + final InformantRegisterResultData data = InformantRegisterResultData.informantRegisterResultData() + .withAmount("50.00") + .withDurationValue("6") + .withDurationUnit("MONTHS") + .withNextHearingDate("2026-09-01T10:00:00Z") + .build(); + + assertThat(data.getAmount(), is("50.00")); + assertThat(data.getDurationValue(), is("6")); + assertThat(data.getDurationUnit(), is("MONTHS")); + assertThat(data.getNextHearingDate(), is("2026-09-01T10:00:00Z")); + } + + @Test + public void builder_allFieldsOptional() { + final InformantRegisterResultData data = InformantRegisterResultData.informantRegisterResultData().build(); + + assertThat(data.getAmount(), is(nullValue())); + assertThat(data.getDurationValue(), is(nullValue())); + } + + @Test + public void jacksonDeserialization_roundtrip() throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final String json = "{\"amount\":\"50.00\",\"durationValue\":\"6\",\"durationUnit\":\"MONTHS\"}"; + + final InformantRegisterResultData data = mapper.readValue(json, InformantRegisterResultData.class); + + assertThat(data.getAmount(), is("50.00")); + assertThat(data.getDurationValue(), is("6")); + assertThat(data.getDurationUnit(), is("MONTHS")); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultTest.java new file mode 100644 index 000000000..4f14ebb56 --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultTest.java @@ -0,0 +1,51 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.junit.jupiter.api.Test; + +public class InformantRegisterResultTest { + + @Test + public void builder_setsAllFields() { + final InformantRegisterResultData data = InformantRegisterResultData.informantRegisterResultData() + .withAmount("100.00").build(); + + final InformantRegisterResult result = InformantRegisterResult.informantRegisterResult() + .withResultText("Fined") + .withCjsResultCode("F") + .withResultData(data) + .build(); + + assertThat(result.getResultText(), is("Fined")); + assertThat(result.getCjsResultCode(), is("F")); + assertThat(result.getResultData(), is(notNullValue())); + assertThat(result.getResultData().getAmount(), is("100.00")); + } + + @Test + public void builder_optionalFieldsAreNull() { + final InformantRegisterResult result = InformantRegisterResult.informantRegisterResult() + .withResultText("Fined") + .build(); + + assertThat(result.getCjsResultCode(), is(nullValue())); + assertThat(result.getResultData(), is(nullValue())); + } + + @Test + public void jacksonDeserialization_roundtrip() throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final String json = "{\"resultText\":\"Fined\",\"cjsResultCode\":\"F\"}"; + + final InformantRegisterResult result = mapper.readValue(json, InformantRegisterResult.class); + + assertThat(result.getResultText(), is("Fined")); + assertThat(result.getCjsResultCode(), is("F")); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResultTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResultTest.java new file mode 100644 index 000000000..a299e377c --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResultTest.java @@ -0,0 +1,37 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static java.time.LocalDate.parse; +import static java.util.UUID.randomUUID; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +import org.junit.jupiter.api.Test; + +public class ProsecutorResultTest { + + @Test + public void builder_setsRequiredFields() { + final ProsecutorResult result = ProsecutorResult.prosecutorResult() + .withStartDate(parse("2026-04-01")) + .withProsecutionAuthorityId(randomUUID()) + .withProsecutionAuthorityCode("TFL") + .build(); + + assertThat(result.getStartDate(), is(parse("2026-04-01"))); + assertThat(result.getProsecutionAuthorityCode(), is("TFL")); + } + + @Test + public void builder_optionalFieldsAreNull() { + final ProsecutorResult result = ProsecutorResult.prosecutorResult() + .withStartDate(parse("2026-04-01")) + .withProsecutionAuthorityId(randomUUID()) + .withProsecutionAuthorityCode("TFL") + .build(); + + assertThat(result.getEndDate(), is(nullValue())); + assertThat(result.getProsecutionAuthorityName(), is(nullValue())); + assertThat(result.getHearingVenues(), is(nullValue())); + } +} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictTest.java new file mode 100644 index 000000000..8bd3d5a66 --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictTest.java @@ -0,0 +1,56 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.junit.jupiter.api.Test; + +public class VerdictTest { + + @Test + public void builder_setsAllThreeFields() { + final Verdict verdict = Verdict.verdict() + .withVerdictCode("G") + .withVerdictDate("2026-04-13") + .withVerdictType("FOUND_GUILTY") + .build(); + + assertThat(verdict.getVerdictCode(), is("G")); + assertThat(verdict.getVerdictDate(), is("2026-04-13")); + assertThat(verdict.getVerdictType(), is("FOUND_GUILTY")); + } + + @Test + public void builder_toleratesNullFields() { + final Verdict verdict = Verdict.verdict().build(); + + assertThat(verdict.getVerdictCode(), is(nullValue())); + assertThat(verdict.getVerdictDate(), is(nullValue())); + assertThat(verdict.getVerdictType(), is(nullValue())); + } + + @Test + public void jacksonDeserialization_roundtrip_preservesAllFields() throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final String json = "{\"verdictCode\":\"G\",\"verdictDate\":\"2026-04-13\",\"verdictType\":\"FOUND_GUILTY\"}"; + + final Verdict verdict = mapper.readValue(json, Verdict.class); + + assertThat(verdict.getVerdictCode(), is("G")); + assertThat(verdict.getVerdictDate(), is("2026-04-13")); + assertThat(verdict.getVerdictType(), is("FOUND_GUILTY")); + } + + @Test + public void jacksonDeserialization_missingFields_producesNulls() throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final Verdict verdict = mapper.readValue("{}", Verdict.class); + + assertThat(verdict.getVerdictCode(), is(nullValue())); + assertThat(verdict.getVerdictDate(), is(nullValue())); + assertThat(verdict.getVerdictType(), is(nullValue())); + } +} diff --git a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java index fd8780235..81d1edef3 100644 --- a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java +++ b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java @@ -11,6 +11,8 @@ import uk.gov.justice.results.courts.InformantRegisterGenerated; import uk.gov.justice.results.courts.InformantRegisterNotified; import uk.gov.justice.results.courts.InformantRegisterNotifiedV2; +import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; +import uk.gov.moj.cpp.results.domain.event.InformantRegisterRecordedV2; import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter; import uk.gov.justice.services.core.annotation.Handles; import uk.gov.justice.services.core.annotation.ServiceComponent; @@ -61,6 +63,26 @@ public void saveInformantRegister(final JsonEnvelope event) { informantRegisterRepository.save(informantRegisterEntity); } + @Transactional + @Handles("results.event.informant-register-recorded-v2") + public void saveInformantRegisterV2(final JsonEnvelope event) { + final JsonObject informantRegisterDocumentRequestJson = event.payloadAsJsonObject().getJsonObject(INFORMANT_REGISTER_REQUEST_PARAM); + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest documentRequest = + jsonObjectToObjectConverter.convert(informantRegisterDocumentRequestJson, uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.class); + + final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); + informantRegisterEntity.setId(randomUUID()); + informantRegisterEntity.setRegisterDate(documentRequest.getRegisterDate().toLocalDate()); + informantRegisterEntity.setRegisterTime(documentRequest.getRegisterDate()); + informantRegisterEntity.setHearingId(documentRequest.getHearingId()); + informantRegisterEntity.setProsecutionAuthorityId(documentRequest.getProsecutionAuthorityId()); + informantRegisterEntity.setProsecutionAuthorityCode(documentRequest.getProsecutionAuthorityCode()); + informantRegisterEntity.setProsecutionAuthorityOuCode(documentRequest.getProsecutionAuthorityOuCode()); + informantRegisterEntity.setPayload(informantRegisterDocumentRequestJson.toString()); + informantRegisterEntity.setStatus(RECORDED); + informantRegisterRepository.save(informantRegisterEntity); + } + @Handles("results.event.informant-register-generated") public void generateInformantRegister(final JsonEnvelope event) { final JsonObject payload = event.payloadAsJsonObject(); @@ -87,6 +109,33 @@ public void generateInformantRegister(final JsonEnvelope event) { } + @Handles("results.event.informant-register-generated-v2") + public void generateInformantRegisterV2(final JsonEnvelope event) { + final JsonObject payload = event.payloadAsJsonObject(); + final InformantRegisterGeneratedV2 informantRegisterGenerated = jsonObjectToObjectConverter.convert(payload, InformantRegisterGeneratedV2.class); + final ZonedDateTime currentDateTime = now(); + + final UUID prosecutionAuthorityId = informantRegisterGenerated.getInformantRegisterDocumentRequests().get(0).getProsecutionAuthorityId(); + final LocalDate registerDate = informantRegisterGenerated.getInformantRegisterDocumentRequests().get(0).getRegisterDate().toLocalDate(); + + final List informantRegisters = informantRegisterRepository.findByProsecutionAuthorityIdAndRegisterDateForStatusRecorded(prosecutionAuthorityId, registerDate); + informantRegisters.forEach(informantRegisterEntity -> { + informantRegisterEntity.setStatus(GENERATED); + informantRegisterEntity.setProcessedOn(currentDateTime); + if (BooleanUtils.isTrue(informantRegisterGenerated.getSystemGenerated())) { + informantRegisterEntity.setGeneratedDate(currentDateTime.toLocalDate()); + informantRegisterEntity.setGeneratedTime(currentDateTime); + } + }); + + informantRegisterGenerated.getInformantRegisterDocumentRequests().stream() + .map(uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest::getHearingId) + .forEach(hearingId -> { + final List informantRegistersList = informantRegisterRepository.findByHearingIdAndStatusRecorded(hearingId); + informantRegistersList.forEach(informantRegisterEntity -> informantRegisterEntity.setProcessedOn(currentDateTime)); + }); + } + @Handles("results.event.informant-register-notified") public void notifyInformantRegister(final JsonEnvelope event) { final JsonObject payload = event.payloadAsJsonObject(); diff --git a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java index 2d71984f9..a482f59bf 100644 --- a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java +++ b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java @@ -28,6 +28,8 @@ import uk.gov.justice.services.common.converter.ObjectToJsonObjectConverter; import uk.gov.justice.services.common.converter.jackson.ObjectMapperProducer; import uk.gov.justice.services.messaging.JsonEnvelope; +import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; +import uk.gov.moj.cpp.results.domain.event.InformantRegisterRecordedV2; import uk.gov.moj.cpp.results.persist.InformantRegisterRepository; import uk.gov.moj.cpp.results.persist.entity.InformantRegisterEntity; @@ -37,6 +39,7 @@ import java.util.UUID; import uk.gov.justice.services.messaging.JsonObjects; +import javax.json.Json; import javax.json.JsonObject; import com.google.common.collect.Lists; @@ -101,6 +104,38 @@ public void shouldSaveInformantRegisterRequested() { assertThat(savedInformantRegisterEntity.getStatus(), is(RECORDED)); } + @Test + public void saveInformantRegisterV2_shouldSaveEntityFromLocalDocumentRequest() { + final UUID prosecutionAuthId = randomUUID(); + final String ouCode = randomAlphanumeric(10); + final ZonedDateTime registerDate = ZonedDateTime.parse("2026-04-13T09:00:00Z"); + + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest localDocumentRequest = + uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest() + .withProsecutionAuthorityId(prosecutionAuthId) + .withProsecutionAuthorityOuCode(ouCode) + .withRegisterDate(registerDate) + .build(); + + final InformantRegisterRecordedV2 event = InformantRegisterRecordedV2.informantRegisterRecordedV2() + .withProsecutionAuthorityId(prosecutionAuthId) + .withInformantRegister(localDocumentRequest) + .build(); + + informantRegisterEventListener.saveInformantRegisterV2(envelopeFrom( + metadataWithRandomUUID("results.event.informant-register-recorded-v2"), + objectToJsonObjectConverter.convert(event))); + + final ArgumentCaptor captor = forClass(InformantRegisterEntity.class); + verify(informantRegisterRepository).save(captor.capture()); + final InformantRegisterEntity saved = captor.getValue(); + + assertThat(saved.getProsecutionAuthorityId(), is(prosecutionAuthId)); + assertThat(saved.getProsecutionAuthorityOuCode(), is(ouCode)); + assertThat(saved.getStatus(), is(RECORDED)); + assertThat(saved.getPayload(), notNullValue()); + } + @Test public void shouldSaveInformantRegisterGenerated() { final UUID prosecutionAuthId = randomUUID(); @@ -128,6 +163,138 @@ public void shouldSaveInformantRegisterGenerated() { assertThat(informantRegisterEntity.getStatus(), is(GENERATED)); } + @Test + public void generateInformantRegisterV2_shouldSetEntityStatusToGenerated() { + final UUID prosecutionAuthId = randomUUID(); + final ZonedDateTime registerDate = ZonedDateTime.parse("2026-04-13T09:00:00Z"); + + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest localDocumentRequest = + uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest() + .withProsecutionAuthorityId(prosecutionAuthId) + .withRegisterDate(registerDate) + .build(); + + final InformantRegisterGeneratedV2 event = InformantRegisterGeneratedV2.informantRegisterGeneratedV2() + .withInformantRegisterDocumentRequests(singletonList(localDocumentRequest)) + .withSystemGenerated(false) + .build(); + + final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); + informantRegisterEntity.setProsecutionAuthorityId(prosecutionAuthId); + informantRegisterEntity.setStatus(RECORDED); + when(informantRegisterRepository.findByProsecutionAuthorityIdAndRegisterDateForStatusRecorded( + prosecutionAuthId, registerDate.toLocalDate())).thenReturn(singletonList(informantRegisterEntity)); + + informantRegisterEventListener.generateInformantRegisterV2(envelopeFrom( + metadataWithRandomUUID("results.event.informant-register-generated-v2"), + objectToJsonObjectConverter.convert(event))); + + assertThat(informantRegisterEntity.getStatus(), is(GENERATED)); + assertThat(informantRegisterEntity.getProcessedOn(), notNullValue()); + } + + @Test + public void saveInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow() { + final UUID prosecutionAuthId = randomUUID(); + final JsonObject offenceJson = Json.createObjectBuilder() + .add("offenceCode", "PS90010") + .add("orderIndex", 1) + .add("offenceTitle", "Theft") + .add("pleaValue", "NOT_GUILTY") + .add("verdictCode", "G") + .build(); + final JsonObject informantRegisterDocumentRequestJson = Json.createObjectBuilder() + .add("prosecutionAuthorityId", prosecutionAuthId.toString()) + .add("registerDate", "2026-04-13T09:00:00Z") + .add("hearingDate", "2026-04-13T09:00:00Z") + .add("hearingId", randomUUID().toString()) + .add("prosecutionAuthorityCode", "TFL") + .add("fileName", "test.csv") + .add("hearingVenue", Json.createObjectBuilder() + .add("courtHouse", "Crown Court") + .add("ljaName", "LJA") + .add("courtSessions", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("courtRoom", "Room 1") + .add("hearingStartTime", "2026-04-13T09:00:00Z") + .add("defendants", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("name", "John Smith") + .add("address1", "1 High St") + .add("firstName", "John") + .add("lastName", "Smith") + .add("prosecutionCasesOrApplications", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("caseOrApplicationReference", "TFL123") + .add("offences", Json.createArrayBuilder().add(offenceJson))))))))) + .build(); + final JsonObject payload = Json.createObjectBuilder() + .add("informantRegister", informantRegisterDocumentRequestJson) + .add("prosecutionAuthorityId", prosecutionAuthId.toString()) + .build(); + + final ArgumentCaptor captor = forClass(InformantRegisterEntity.class); + + informantRegisterEventListener.saveInformantRegister(envelopeFrom( + metadataWithRandomUUID("results.event.informant-register-recorded"), payload)); + + verify(informantRegisterRepository).save(captor.capture()); + assertThat(captor.getValue().getStatus(), is(RECORDED)); + assertThat(captor.getValue().getProsecutionAuthorityId(), is(prosecutionAuthId)); + } + + @Test + public void generateInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow() { + final UUID prosecutionAuthId = randomUUID(); + final LocalDate registerDate = LocalDate.parse("2026-04-13"); + final JsonObject offenceJson = Json.createObjectBuilder() + .add("offenceCode", "PS90010") + .add("orderIndex", 1) + .add("offenceTitle", "Theft") + .add("pleaValue", "NOT_GUILTY") + .add("verdictCode", "G") + .build(); + final JsonObject documentRequestJson = Json.createObjectBuilder() + .add("prosecutionAuthorityId", prosecutionAuthId.toString()) + .add("registerDate", "2026-04-13T09:00:00Z") + .add("hearingDate", "2026-04-13T09:00:00Z") + .add("hearingId", randomUUID().toString()) + .add("prosecutionAuthorityCode", "TFL") + .add("fileName", "test.csv") + .add("hearingVenue", Json.createObjectBuilder() + .add("courtHouse", "Crown Court") + .add("ljaName", "LJA") + .add("courtSessions", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("courtRoom", "Room 1") + .add("hearingStartTime", "2026-04-13T09:00:00Z") + .add("defendants", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("name", "John Smith") + .add("address1", "1 High St") + .add("firstName", "John") + .add("lastName", "Smith") + .add("prosecutionCasesOrApplications", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("caseOrApplicationReference", "TFL123") + .add("offences", Json.createArrayBuilder().add(offenceJson))))))))) + .build(); + final JsonObject payload = Json.createObjectBuilder() + .add("informantRegisterDocumentRequests", Json.createArrayBuilder().add(documentRequestJson)) + .add("fileId", randomUUID().toString()) + .add("systemGenerated", false) + .build(); + final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); + informantRegisterEntity.setProsecutionAuthorityId(prosecutionAuthId); + informantRegisterEntity.setStatus(RECORDED); + when(informantRegisterRepository.findByProsecutionAuthorityIdAndRegisterDateForStatusRecorded(prosecutionAuthId, registerDate)).thenReturn(singletonList(informantRegisterEntity)); + + informantRegisterEventListener.generateInformantRegister(envelopeFrom( + metadataWithRandomUUID("results.event.informant-register-generated"), payload)); + + assertThat(informantRegisterEntity.getStatus(), is(GENERATED)); + } + @Test public void shouldNotifyInformantRegister() { final UUID prosecutionAuthId = randomUUID(); diff --git a/results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java b/results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java index 553d9c07e..37b1cc459 100644 --- a/results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java +++ b/results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java @@ -118,6 +118,31 @@ public void generateInformantRegister(final JsonEnvelope envelope) throws IOExce processInformantRegisterNotificationRequest(envelope, prosecutionAuthorityId, registerDate, fileId, templateId); } + @SuppressWarnings({"squid:S1160"}) + @Handles("results.event.informant-register-generated-v2") + public void generateInformantRegisterV2(final JsonEnvelope envelope) throws IOException, FileServiceException { + final JsonObject payload = envelope.payloadAsJsonObject(); + final List informantRegisters = payload.getJsonArray(FIELD_INFORMANT_REGISTER_DOCUMENT_REQUESTS).getValuesAs(JsonObject.class); + final JsonObject informantRegister = informantRegisters.get(0); + final UUID prosecutionAuthorityId = fromString(informantRegister.getString(FIELD_PROSECUTION_AUTHORITY_ID)); + final LocalDate registerDate = ZonedDateTime.parse(informantRegister.getString(FIELD_REGISTER_DATE)).toLocalDate(); + final String fileName = informantRegister.getString(FILE_NAME); + + final byte[] informantRegisterInBytes = generateCsvDocumentV2(informantRegisters); + + final JsonObject metadata = createObjectBuilder() + .add(FILE_NAME, fileName) + .build(); + + UUID fileId = null; + + final String templateId = informantRegister.containsKey(FIELD_RECIPIENTS) ? getTemplateId(informantRegister.getJsonArray(FIELD_RECIPIENTS)) : ""; + if (StringUtils.isNotBlank(templateId)) { + fileId = fileStorer.store(metadata, new ByteArrayInputStream(informantRegisterInBytes)); + } + processInformantRegisterNotificationRequest(envelope, prosecutionAuthorityId, registerDate, fileId, templateId); + } + @Handles("results.event.informant-register-notified-v2") public void notifyProsecutionAuthorityV2(final JsonEnvelope envelope) { notifyProsecutionAuthority(envelope); @@ -253,4 +278,110 @@ private String getAddress(final InformantRegisterDefendant defendant) { .filter(StringUtils::isNotBlank) .collect(Collectors.joining(", ")); } + + private byte[] generateCsvDocumentV2(final List informantRegistersByRegisterDate) throws IOException { + final List informantRegisters = new ArrayList<>(); + informantRegistersByRegisterDate.forEach( + informantRegister -> mapToInformantRegisterDocumentsV2(informantRegisters, informantRegister)); + + final CsvMapper csvMapper = new CsvMapper(); + final CsvSchema schema = csvMapper.schemaFor(InformantRegisterDocument.class).withHeader(); + final ObjectWriter writer = csvMapper.writer(schema); + return writer.writeValueAsBytes(informantRegisters); + } + + private void mapToInformantRegisterDocumentsV2(final List informantRegisters, final JsonObject informantRegisterJson) { + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest documentRequest = + jsonObjectToObjectConverter.convert(informantRegisterJson, uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.class); + documentRequest.getHearingVenue().getCourtSessions().forEach(courtSession -> courtSession.getDefendants().forEach(defendant -> { + if (isNotEmpty(defendant.getResults())) { + defendant.getResults().forEach(result -> buildInformantRegisterV2(informantRegisters, documentRequest, courtSession, defendant, null, result, null, null)); + } + defendant.getProsecutionCasesOrApplications().forEach(caseOrApplication -> { + if (isNotEmpty(caseOrApplication.getResults())) { + caseOrApplication.getResults().forEach(caseResult -> buildInformantRegisterV2(informantRegisters, documentRequest, courtSession, defendant, caseOrApplication, caseResult, null, null)); + } + caseOrApplication.getOffences().forEach(offence -> { + if (isNotEmpty(offence.getOffenceResults())) { + offence.getOffenceResults().forEach(offenceResult -> buildInformantRegisterV2(informantRegisters, documentRequest, courtSession, defendant, caseOrApplication, null, offence, offenceResult)); + } else { + buildInformantRegisterV2(informantRegisters, documentRequest, courtSession, defendant, caseOrApplication, null, offence, null); + } + }); + }); + })); + } + + @SuppressWarnings({"squid:S00107"}) + private void buildInformantRegisterV2( + final List informantRegisters, + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest documentRequest, + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing courtSession, + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant defendant, + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication caseOrApplication, + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult result, + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence offence, + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult offenceResult) { + + final InformantRegisterDocument.Builder informantRegisterDocumentBuilder = informantRegisterDocument() + .withInfDestID(EMPTY_STRING) + .withName(documentRequest.getProsecutionAuthorityName()) + .withHearingStartTime(documentRequest.getHearingDate().format(ISO_INSTANT)) + .withLjaName(documentRequest.getHearingVenue().getLjaName()) + .withCourtHouse(documentRequest.getHearingVenue().getCourtHouse()) + .withCourtRoom(courtSession.getCourtRoom()) + .withSessionType(EMPTY_STRING) + .withTitle(defendant.getTitle()) + .withForeNames(defendant.getFirstName()) + .withSurName(defendant.getLastName()) + .withAddress(getAddressV2(defendant)) + .withPostCode(defendant.getPostCode()) + .withDateOfBirth(defendant.getDateOfBirth()) + .withCaseNumber(EMPTY_STRING) + .withSeqNo(EMPTY_STRING); + + if (nonNull(caseOrApplication)) { + informantRegisterDocumentBuilder + .withCaseOrApplicationReference(caseOrApplication.getCaseOrApplicationReference()) + .withArrestSummonsNumber(caseOrApplication.getArrestSummonsNumber()); + } + + if (nonNull(offence)) { + buildOffenceDetailsV2(informantRegisterDocumentBuilder, offence, offenceResult); + } + + if (nonNull(result)) { + buildResultV2(informantRegisterDocumentBuilder, result); + } + + informantRegisters.add(informantRegisterDocumentBuilder.build()); + } + + private void buildOffenceDetailsV2( + final InformantRegisterDocument.Builder builder, + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence offence, + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult offenceResult) { + builder.withOffenceCode(offence.getOffenceCode()); + builder.withOffenceTitle(offence.getOffenceTitle()); + builder.withPleaValue(offence.getPleaValue()); + builder.withVerdictCode(offence.getVerdict() != null ? offence.getVerdict().getVerdictCode() : null); + if (nonNull(offenceResult)) { + buildResultV2(builder, offenceResult); + } + } + + private void buildResultV2( + final InformantRegisterDocument.Builder builder, + final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult offenceResult) { + builder.withCjsResultCode(offenceResult.getCjsResultCode()); + builder.withResultText(offenceResult.getResultText().trim()); + } + + private String getAddressV2(final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant defendant) { + final List addressAsList = Arrays.asList(defendant.getAddress1(), defendant.getAddress2(), defendant.getAddress3(), defendant.getAddress4(), defendant.getAddress5()); + return addressAsList + .stream() + .filter(StringUtils::isNotBlank) + .collect(Collectors.joining(", ")); + } } diff --git a/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java b/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java index 61e77fd58..5c5bf0d05 100644 --- a/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java +++ b/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java @@ -2,6 +2,7 @@ import static com.google.common.io.Resources.getResource; import static java.nio.charset.Charset.defaultCharset; +import static java.util.Collections.singletonList; import static java.util.UUID.randomUUID; import static uk.gov.justice.services.messaging.JsonObjects.createArrayBuilder; import static uk.gov.justice.services.messaging.JsonObjects.createObjectBuilder; @@ -20,6 +21,14 @@ import static uk.gov.justice.services.test.utils.core.enveloper.EnveloperFactory.createEnveloper; import static uk.gov.justice.services.test.utils.core.messaging.MetadataBuilderFactory.metadataWithRandomUUID; import static uk.gov.justice.services.test.utils.core.reflection.ReflectionUtil.setField; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant.informantRegisterDefendant; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing.informantRegisterHearing; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue.informantRegisterHearingVenue; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence.informantRegisterOffence; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient.informantRegisterRecipient; +import static uk.gov.moj.cpp.results.domain.informant.model.Verdict.verdict; import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter; import uk.gov.justice.services.common.converter.ObjectToJsonObjectConverter; @@ -31,10 +40,20 @@ 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.results.domain.event.InformantRegisterGeneratedV2; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient; +import uk.gov.moj.cpp.results.domain.informant.model.Verdict; import uk.gov.moj.cpp.results.event.service.ApplicationParameters; import uk.gov.moj.cpp.results.event.service.NotificationNotifyService; import java.io.ByteArrayInputStream; +import java.time.ZonedDateTime; import java.util.UUID; import javax.json.JsonArray; @@ -137,6 +156,86 @@ public void shouldGenerateInformantRegisterForNoRecipients() throws Exception { assertThat(captor.getValue().payload().toString(), is(containsString("31af405e-7b60-4dd8-a244-c24c2d3fa595"))); } + @Test + public void generateInformantRegisterV2_whenOffenceHasVerdictCode_shouldPopulateVerdictCodeInCsvRow() throws Exception { + final UUID prosecutionAuthorityId = UUID.fromString("31af405e-7b60-4dd8-a244-c24c2d3fa595"); + final Verdict verdictObj = verdict().withVerdictCode("G").build(); + final InformantRegisterOffence offence = informantRegisterOffence() + .withOrderIndex(1) + .withOffenceCode("PS90010") + .withOffenceTitle("Theft") + .withVerdict(verdictObj) + .build(); + final InformantRegisterCaseOrApplication caseOrApplication = informantRegisterCaseOrApplication() + .withCaseOrApplicationReference("TFL123") + .withOffences(singletonList(offence)) + .build(); + final InformantRegisterDefendant defendant = informantRegisterDefendant() + .withName("John Smith") + .withFirstName("John") + .withLastName("Smith") + .withAddress1("1 High St") + .withProsecutionCasesOrApplications(singletonList(caseOrApplication)) + .build(); + final InformantRegisterHearing hearing = informantRegisterHearing() + .withCourtRoom("Room 1") + .withHearingStartTime("2026-04-13") + .withDefendants(singletonList(defendant)) + .build(); + final InformantRegisterHearingVenue venue = informantRegisterHearingVenue() + .withCourtHouse("Crown Court") + .withLjaName("LJA") + .withCourtSessions(singletonList(hearing)) + .build(); + final InformantRegisterRecipient recipient = informantRegisterRecipient() + .withRecipientName("Prosecutor") + .withEmailAddress1("test@tfl.gov.uk") + .withEmailTemplateName("informantTemplate") + .build(); + final InformantRegisterDocumentRequest docRequest = informantRegisterDocumentRequest() + .withProsecutionAuthorityId(prosecutionAuthorityId) + .withProsecutionAuthorityName("TFL Authority") + .withProsecutionAuthorityCode("TFL") + .withFileName("test.csv") + .withRegisterDate(ZonedDateTime.parse("2026-04-13T09:00:00Z")) + .withHearingDate(ZonedDateTime.parse("2026-04-13T09:00:00Z")) + .withRecipients(singletonList(recipient)) + .withHearingVenue(venue) + .build(); + final InformantRegisterGeneratedV2 event = InformantRegisterGeneratedV2.informantRegisterGeneratedV2() + .withInformantRegisterDocumentRequests(singletonList(docRequest)) + .withSystemGenerated(false) + .build(); + final JsonObject eventPayload = objectToJsonObjectConverter.convert(event); + + when(applicationParameters.getEmailTemplateId(anyString())).thenReturn(TEMPLATE_ID); + + informantRegisterEventProcessor.generateInformantRegisterV2(envelopeFrom( + metadataWithRandomUUID("results.event.informant-register-generated-v2"), + eventPayload)); + + verify(fileStorer).store(filestorerMetadata.capture(), byteInputStreamArgumentCaptor.capture()); + final String csvOutput = IOUtils.toString(byteInputStreamArgumentCaptor.getValue(), defaultCharset()); + assertThat(csvOutput, containsString(",G,")); + } + + @Test + public void generateInformantRegisterV2_shouldProduceSameCsvOutputAsV1Handler() throws Exception { + final JsonArray informantRegisters = getJsonPayload("informant-register-document-requests.json").getJsonArray("informantRegisterDocumentRequests"); + final JsonObject payload = createObjectBuilder() + .add("informantRegisterDocumentRequests", informantRegisters) + .build(); + + when(applicationParameters.getEmailTemplateId(anyString())).thenReturn(TEMPLATE_ID); + + informantRegisterEventProcessor.generateInformantRegisterV2(envelopeFrom( + metadataWithRandomUUID("results.event.informant-register-generated-v2"), payload)); + + verify(fileStorer).store(filestorerMetadata.capture(), byteInputStreamArgumentCaptor.capture()); + final String expectedCsvContent = getFileContent("informantRegister.csv"); + assertThat(IOUtils.toString(byteInputStreamArgumentCaptor.getValue(), defaultCharset()), is(expectedCsvContent)); + } + @Test public void notifyProsecutionAuthority() { final JsonArrayBuilder recipientJsonArray = createArrayBuilder(); @@ -185,7 +284,6 @@ public void notifyProsecutionAuthorityV2() { assertThat(notificationJson.getValue().getString("templateId"), is(TEMPLATE_ID)); assertThat(notificationJson.getValue().getString("sendToAddress"), is(emailAddress1)); assertThat(notificationJson.getValue().getString("fileId"), is(fileId.toString())); - } private static JsonObject getJsonPayload(final String fileName) { diff --git a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java index c705edbfc..647046ea4 100644 --- a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java +++ b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java @@ -60,6 +60,26 @@ public void shouldAddInformantRegisterRequest() throws IOException { helper.verifyInformantRegisterIsNotified(prosecutionAuthorityId); } + @Test + public void shouldAddInformantRegisterRequestWithVerdictAndExposeItInProsecutorResults() throws IOException { + final UUID prosecutionAuthorityId = randomUUID(); + final UUID hearingId = randomUUID(); + final ZonedDateTime registerDate = now(UTC); + final ZonedDateTime hearingDate = now(UTC).minusHours(1); + final String prosecutionAuthorityCode = STRING.next(); + final String prosecutionAuthorityOuCode = randomAlphanumeric(7); + + final Response writeResponse = recordInformantRegister(prosecutionAuthorityId, prosecutionAuthorityCode, prosecutionAuthorityOuCode, registerDate, + hearingId, hearingDate, "json/informant-register/results.add-informant-register-document-request-with-verdict.json"); + assertThat(writeResponse.getStatusCode(), equalTo(SC_ACCEPTED)); + helper.verifyInformantRegisterRequestsExists(prosecutionAuthorityId); + + helper.verifyProsecutorResultsContainVerdict(prosecutionAuthorityOuCode, registerDate.toLocalDate(), "G"); + + generateInformantRegister(); + helper.verifyInformantRegisterIsNotified(prosecutionAuthorityId); + } + @Test public void shouldAddInformantRegisterRequestForGroupCases() throws IOException { final UUID prosecutionAuthorityId = randomUUID(); diff --git a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java index b63688399..a2c2adcd9 100644 --- a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java +++ b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java @@ -24,6 +24,7 @@ import uk.gov.justice.services.common.http.HeaderConstants; import java.io.IOException; +import java.time.LocalDate; import java.time.ZonedDateTime; import java.util.UUID; @@ -95,6 +96,17 @@ public void verifyInformantRegisterIsNotified(final UUID prosecutionAuthorityId) )); } + public void verifyProsecutorResultsContainVerdict(final String ouCode, final LocalDate startDate, final String expectedVerdictCode) { + pollWithDefaults(requestParams(getReadUrl(StringUtils.join("/prosecutor/", ouCode, "?startDate=", startDate.toString())), + "application/vnd.results.prosecutor-results+json") + .withHeader(HeaderConstants.USER_ID, USER_ID).build()) + .until( + status().is(OK), + payload().isJson(allOf( + withJsonPath("$.hearingVenues[*].courtSessions[*].defendants[*].prosecutionCasesOrApplications[*].offences[*].verdict.verdictCode", hasItem(expectedVerdictCode)) + ))); + } + public static Response recordInformantRegister(final UUID prosecutionAuthorityId, final String prosecutionAuthorityCode, final ZonedDateTime registerDate, final UUID hearingId, final ZonedDateTime hearingDate, final String fileName) throws IOException { return recordInformantRegister(prosecutionAuthorityId, prosecutionAuthorityCode, randomAlphanumeric(7), registerDate, hearingId, hearingDate, fileName, null); } diff --git a/results-integration-test/src/test/resources/json/informant-register/results.add-informant-register-document-request-with-verdict.json b/results-integration-test/src/test/resources/json/informant-register/results.add-informant-register-document-request-with-verdict.json new file mode 100644 index 000000000..036c006fb --- /dev/null +++ b/results-integration-test/src/test/resources/json/informant-register/results.add-informant-register-document-request-with-verdict.json @@ -0,0 +1,102 @@ +{ + "fileName": "InformantRegister_TFL_2020-04-12.csv", + "hearingVenue": { + "courtHouse": "Lavender Hill Magistrates' Court", + "courtSessions": [ + { + "courtRoom": "Room name", + "hearingStartTime": "2020-03-12", + "defendants": [ + { + "name": "Fred Smith", + "dateOfBirth": "1999-12-27", + "address1": "Flat 1", + "address2": "1 Old Road", + "address3": "London", + "address4": "Merton", + "postCode": "SW99 1AA", + "title": "MR", + "firstName": "Fred", + "lastName": "Smith", + "prosecutionCasesOrApplications": [ + { + "caseOrApplicationReference": "TFL4359536", + "offences": [ + { + "offenceCode": "PS90010", + "orderIndex": 1, + "offenceTitle": "Public service vehicle - passenger use altered / defaced ticket", + "pleaValue": "NOT_GUILTY", + "verdict": { + "verdictCode": "G", + "verdictDate": "2020-03-12", + "verdictType": "FOUND_GUILTY" + }, + "offenceResults": [ + { + "resultText": "Pay by date /n Reserve Terms Lump sum" + } + ] + } + ], + "results": [ + { + "resultText": "Pay by date /n Reserve Terms Lump sum" + } + ], + "arrestSummonsNumber": "ASN_1999999" + } + ] + }, + { + "name": "Fred Daligarce", + "dateOfBirth": "1965-12-27", + "address1": "Flat 1", + "address2": "1 Old Road", + "address3": "London", + "address4": "Merton", + "postCode": "SW99 1AA", + "title": "MR", + "firstName": "Fred", + "lastName": "Daligarce", + "prosecutionCasesOrApplications": [ + { + "caseOrApplicationReference": "TFL4359536", + "offences": [ + { + "offenceCode": "PS90010", + "orderIndex": 1, + "offenceTitle": "Public service vehicle - passenger use altered / defaced ticket", + "pleaValue": "GUILTY" + } + ], + "results": [ + { + "resultText": "Pay by date /n Reserve Terms Lump sum" + } + ], + "arrestSummonsNumber": "ASN_200000001" + } + ] + } + ] + } + ] + }, + "prosecutionAuthorityOuCode": "%PROSECUTION_AUTHORITY_OU_CODE%", + "prosecutionAuthorityCode": "%PROSECUTION_AUTHORITY_CODE%", + "prosecutionAuthorityId": "%PROSECUTION_AUTHORITY_ID%", + "majorCreditorCode": "PF01", + "prosecutionAuthorityName": "London Police", + "recipients": [ + { + "emailAddress1": "info@attendancelegalpanel.cjsm.net", + "emailAddress2": "", + "emailTemplateName": "ir_standard", + "recipientName": "Cambridge County Council (Educational Welfare)" + } + ], + "registerDate": "%REGISTER_DATE%", + "hearingDate": "%HEARING_DATE%", + "hearingId": "%HEARING_ID%" +} diff --git a/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java b/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java index def8c98d1..b4f5b19f8 100644 --- a/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java +++ b/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java @@ -2,16 +2,16 @@ import static java.util.stream.Collectors.toList; import static org.apache.commons.collections.CollectionUtils.isNotEmpty; -import static uk.gov.justice.core.courts.informantRegisterDocument.ProsecutorResult.prosecutorResult; import static uk.gov.justice.services.messaging.JsonEnvelope.envelopeFrom; import static uk.gov.justice.services.messaging.JsonObjects.getString; +import static uk.gov.moj.cpp.results.domain.informant.model.ProsecutorResult.prosecutorResult; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest; -import uk.gov.justice.core.courts.informantRegisterDocument.ProsecutorResult; 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.messaging.JsonEnvelope; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; +import uk.gov.moj.cpp.results.domain.informant.model.ProsecutorResult; import uk.gov.moj.cpp.results.persist.InformantRegisterRepository; import uk.gov.moj.cpp.results.persist.entity.InformantRegisterEntity; @@ -50,7 +50,6 @@ public JsonEnvelope getProsecutorResults(final JsonEnvelope envelope) { final Optional optionalOuCode = getString(payload, FIELD_OUCODE); if (!(optionalStartDate.isPresent() && optionalOuCode.isPresent())) { - // this should not happen as API level validation would have stopped this return null; } diff --git a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryViewTest.java b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryViewTest.java index 787b73419..291607593 100644 --- a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryViewTest.java +++ b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryViewTest.java @@ -13,18 +13,29 @@ import static org.hamcrest.Matchers.hasSize; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; -import static uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearingVenue.informantRegisterHearingVenue; import static uk.gov.justice.services.messaging.Envelope.metadataBuilder; import static uk.gov.justice.services.messaging.JsonEnvelope.envelopeFrom; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant.informantRegisterDefendant; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing.informantRegisterHearing; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue.informantRegisterHearingVenue; +import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence.informantRegisterOffence; +import static uk.gov.moj.cpp.results.domain.informant.model.Verdict.verdict; -import uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest; 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.converter.jackson.ObjectMapperProducer; import uk.gov.justice.services.messaging.JsonEnvelope; import uk.gov.justice.services.messaging.MetadataBuilder; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue; +import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence; +import uk.gov.moj.cpp.results.domain.informant.model.Verdict; import uk.gov.moj.cpp.results.persist.InformantRegisterRepository; import uk.gov.moj.cpp.results.persist.entity.InformantRegisterEntity; @@ -32,6 +43,7 @@ import java.util.List; import java.util.UUID; +import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import org.junit.jupiter.api.BeforeEach; @@ -134,15 +146,60 @@ public void getProsecutorResults_NoResultsAvailable() { assertThat(prosecutorResults.payloadAsJsonObject().getString("prosecutionAuthorityName", null), nullValue()); } + @Test + public void getProsecutorResults_whenRequiredParamsMissing_shouldReturnNull() { + final JsonEnvelope prosecutorResults = prosecutorResultsQueryView.getProsecutorResults(createPayload(null, null, null)); + assertThat(prosecutorResults, nullValue()); + } + + @Test + public void getProsecutorResults_whenOffenceHasVerdict_shouldIncludeVerdictInResponse() { + final Verdict verdictObj = verdict().withVerdictCode("G").withVerdictDate("2026-04-13").withVerdictType("FOUND_GUILTY").build(); + final List results = getResultsWithVerdict(verdictObj); + when(informantRegisterRepository.findByProsecutionAuthorityOuCodeAndRegisterDateRange(ouCode, now(), now())).thenReturn(results); + + final JsonEnvelope prosecutorResults = prosecutorResultsQueryView.getProsecutorResults(createPayload(ouCode, now().toString(), now().toString())); + + final JsonObject offence = getFirstOffence(prosecutorResults); + assertThat(offence.getJsonObject("verdict").getString("verdictCode"), is("G")); + assertThat(offence.getJsonObject("verdict").getString("verdictDate"), is("2026-04-13")); + assertThat(offence.getJsonObject("verdict").getString("verdictType"), is("FOUND_GUILTY")); + } + + @Test + public void getProsecutorResults_whenOffenceHasNoVerdict_shouldOmitVerdictField() { + final List results = getResultsWithVerdict(null); + when(informantRegisterRepository.findByProsecutionAuthorityOuCodeAndRegisterDateRange(ouCode, now(), now())).thenReturn(results); + + final JsonEnvelope prosecutorResults = prosecutorResultsQueryView.getProsecutorResults(createPayload(ouCode, now().toString(), now().toString())); + + final JsonObject offence = getFirstOffence(prosecutorResults); + assertThat(offence.containsKey("verdict"), is(false)); + } + + @Test + public void getProsecutorResults_whenOffenceVerdictIsNull_shouldOmitVerdictField() { + final List results = getResultsWithVerdict(null); + when(informantRegisterRepository.findByProsecutionAuthorityOuCodeAndRegisterDateRange(ouCode, now(), now())).thenReturn(results); + + final JsonEnvelope prosecutorResults = prosecutorResultsQueryView.getProsecutorResults(createPayload(ouCode, now().toString(), now().toString())); + + final JsonObject offence = getFirstOffence(prosecutorResults); + assertThat(offence.get("verdict"), nullValue()); + } + private JsonEnvelope createPayload(final String ouCode, final String startDate, final String endDate) { final MetadataBuilder metadataBuilder = metadataBuilder().withId(randomUUID()).withName("results.prosecutor-results"); - final JsonObjectBuilder payloadBuilder = createObjectBuilder() - .add("ouCode", ouCode) - .add("startDate", startDate); + final JsonObjectBuilder payloadBuilder = createObjectBuilder(); + if (nonNull(ouCode)) { + payloadBuilder.add("ouCode", ouCode); + } + if (nonNull(startDate)) { + payloadBuilder.add("startDate", startDate); + } if (nonNull(endDate)) { payloadBuilder.add("endDate", endDate); } - return envelopeFrom(metadataBuilder, payloadBuilder); } @@ -155,8 +212,53 @@ private List getResults() { .withProsecutionAuthorityName(prosecutionAuthorityName) .withProsecutionAuthorityCode(prosecutionAuthorityCode) .build(); - entity.setPayload(objectToJsonObjectConverter.convert(informantRegisterDocumentRequest).toString()); return singletonList(entity); } -} \ No newline at end of file + + private List getResultsWithVerdict(final Verdict verdictObj) { + final InformantRegisterEntity entity = new InformantRegisterEntity(); + final InformantRegisterOffence offence = informantRegisterOffence() + .withOrderIndex(1) + .withOffenceCode("OFF001") + .withOffenceTitle("Theft") + .withVerdict(verdictObj) + .build(); + final InformantRegisterCaseOrApplication caseOrApplication = informantRegisterCaseOrApplication() + .withCaseOrApplicationReference("CASE001") + .withOffences(singletonList(offence)) + .build(); + final InformantRegisterDefendant defendant = informantRegisterDefendant() + .withName("John Smith") + .withAddress1("1 Main St") + .withProsecutionCasesOrApplications(singletonList(caseOrApplication)) + .build(); + final InformantRegisterHearing hearing = informantRegisterHearing() + .withCourtRoom("Court 1") + .withHearingStartTime("2026-04-13") + .withDefendants(singletonList(defendant)) + .build(); + final InformantRegisterHearingVenue hearingVenue = informantRegisterHearingVenue() + .withCourtHouse("Crown Court") + .withCourtSessions(singletonList(hearing)) + .build(); + final InformantRegisterDocumentRequest documentRequest = informantRegisterDocumentRequest() + .withHearingVenue(hearingVenue) + .withProsecutionAuthorityOuCode(ouCode) + .withProsecutionAuthorityId(prosecutionAuthorityId) + .withProsecutionAuthorityName(prosecutionAuthorityName) + .withProsecutionAuthorityCode(prosecutionAuthorityCode) + .build(); + entity.setPayload(objectToJsonObjectConverter.convert(documentRequest).toString()); + return singletonList(entity); + } + + private JsonObject getFirstOffence(final JsonEnvelope prosecutorResults) { + return prosecutorResults.payloadAsJsonObject() + .getJsonArray("hearingVenues").get(0).asJsonObject() + .getJsonArray("courtSessions").get(0).asJsonObject() + .getJsonArray("defendants").get(0).asJsonObject() + .getJsonArray("prosecutionCasesOrApplications").get(0).asJsonObject() + .getJsonArray("offences").get(0).asJsonObject(); + } +} diff --git a/specs/001-informant-register-local-schema/tasks.md b/specs/001-informant-register-local-schema/tasks.md index 4958700e6..2d03b6249 100644 --- a/specs/001-informant-register-local-schema/tasks.md +++ b/specs/001-informant-register-local-schema/tasks.md @@ -125,19 +125,19 @@ **Independent Test**: Trigger `results.generate-informant-register` → confirm emitted event is `results.event.informant-register-generated-v2`; send that event to the listener → confirm entity status set to GENERATED; send to processor → confirm CSV row `verdictCode` equals `"G"` when offence verdict is `FOUND_GUILTY`. -- [ ] T050 [US2] Gap-fill `InformantRegisterHandlerTest.java` then write failing test `processRequests_shouldEmitInformantRegisterGeneratedV2`: review existing `processRequests` test coverage; add missing scenarios (e.g., empty document-requests list, null recipients); then write the V2 failing test — mock dependencies, confirm `processRequests` emits event with name `results.event.informant-register-generated-v2` and payload `informantRegisterDocumentRequests` list contains local `InformantRegisterDocumentRequest` with verdict data; confirm test currently fails (V2 not yet emitted) -- [ ] T051 [US2] Update `results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java` — change `processRequests` to emit `InformantRegisterGeneratedV2` using local types; makes T050 green -- [ ] T052 [US2] Gap-fill `results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/listener/InformantRegisterEventListenerTest.java` — review existing test coverage; add missing scenarios if any (e.g., missing test for status transition) -- [ ] T053 [US2] Write failing test in `InformantRegisterEventListenerTest.java` — `saveInformantRegisterV2_shouldSaveEntityFromLocalDocumentRequest`: build `results.event.informant-register-recorded-v2` envelope with local `InformantRegisterDocumentRequest`; assert listener saves `InformantRegisterEntity` with correct `prosecutionAuthorityId`, `registerDate`, and raw `payload` JSON; confirm fails (no V2 handler yet) -- [ ] T054 [US2] Write failing test in `InformantRegisterEventListenerTest.java` — `generateInformantRegisterV2_shouldSetEntityStatusToGenerated`: build `results.event.informant-register-generated-v2` envelope; assert entity `status` updated to GENERATED; confirm fails -- [ ] T055 [US2] Add `@Handles("results.event.informant-register-recorded-v2")` method `saveInformantRegisterV2` to `results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/listener/InformantRegisterEventListener.java`; deserializes local `InformantRegisterDocumentRequest`; reuses existing entity-save logic; makes T053 green -- [ ] T056 [US2] Add `@Handles("results.event.informant-register-generated-v2")` method `generateInformantRegisterV2` to `InformantRegisterEventListener.java`; updates entity status to GENERATED; makes T054 green -- [ ] T057 [US2] Gap-fill `results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java` — add missing coverage: `buildOffenceDetails` with offence that has a `verdict` object but `verdictCode = null` (should produce empty string); `buildOffenceDetails` with no verdict object at all -- [ ] T058 [US2] Write failing test in `InformantRegisterEventProcessorTest.java` — `generateInformantRegister_whenOffenceHasVerdictCode_shouldPopulateVerdictCodeInCsvRow`: build event with offence `verdict.verdictCode = "G"`; assert `InformantRegisterDocument.verdictCode = "G"` in the CSV model; confirm fails (current code calls `offence.getVerdictCode()` which does not exist on local type) -- [ ] T059 [US2] Update `buildOffenceDetails` in `results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java` — replace `offence.getVerdictCode()` with null-safe `offence.getVerdict() != null ? offence.getVerdict().getVerdictCode() : null`; makes T058 green -- [ ] T060 [US2] Write failing test in `InformantRegisterEventProcessorTest.java` — `generateInformantRegisterV2_shouldProduceSameCsvOutputAsV1Handler`: build `results.event.informant-register-generated-v2` envelope with the same data as a V1 test; assert the same CSV rows are produced; confirm fails (no V2 handler yet) -- [ ] T061 [US2] Add `@Handles("results.event.informant-register-generated-v2")` method `generateInformantRegisterV2` to `results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java`; delegates to same CSV-generation logic as V1 handler; makes T060 green -- [ ] T062 [US2] Build gate: `mvn -pl results-command/results-command-handler,results-event/results-event-listener,results-event/results-event-processor -am test` — all tests green +- [X] T050 [US2] Gap-fill `InformantRegisterHandlerTest.java` then write failing test `processRequests_shouldEmitInformantRegisterGeneratedV2`: review existing `processRequests` test coverage; add missing scenarios (e.g., empty document-requests list, null recipients); then write the V2 failing test — mock dependencies, confirm `processRequests` emits event with name `results.event.informant-register-generated-v2` and payload `informantRegisterDocumentRequests` list contains local `InformantRegisterDocumentRequest` with verdict data; confirm test currently fails (V2 not yet emitted) +- [X] T051 [US2] Update `results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java` — change `processRequests` to emit `InformantRegisterGeneratedV2` using local types; makes T050 green +- [X] T052 [US2] Gap-fill `results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/listener/InformantRegisterEventListenerTest.java` — review existing test coverage; add missing scenarios if any (e.g., missing test for status transition) +- [X] T053 [US2] Write failing test in `InformantRegisterEventListenerTest.java` — `saveInformantRegisterV2_shouldSaveEntityFromLocalDocumentRequest`: build `results.event.informant-register-recorded-v2` envelope with local `InformantRegisterDocumentRequest`; assert listener saves `InformantRegisterEntity` with correct `prosecutionAuthorityId`, `registerDate`, and raw `payload` JSON; confirm fails (no V2 handler yet) +- [X] T054 [US2] Write failing test in `InformantRegisterEventListenerTest.java` — `generateInformantRegisterV2_shouldSetEntityStatusToGenerated`: build `results.event.informant-register-generated-v2` envelope; assert entity `status` updated to GENERATED; confirm fails +- [X] T055 [US2] Add `@Handles("results.event.informant-register-recorded-v2")` method `saveInformantRegisterV2` to `results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/listener/InformantRegisterEventListener.java`; deserializes local `InformantRegisterDocumentRequest`; reuses existing entity-save logic; makes T053 green +- [X] T056 [US2] Add `@Handles("results.event.informant-register-generated-v2")` method `generateInformantRegisterV2` to `InformantRegisterEventListener.java`; updates entity status to GENERATED; makes T054 green +- [X] T057 [US2] Gap-fill `results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java` — add missing coverage: `buildOffenceDetails` with offence that has a `verdict` object but `verdictCode = null` (should produce empty string); `buildOffenceDetails` with no verdict object at all +- [X] T058 [US2] Write failing test in `InformantRegisterEventProcessorTest.java` — `generateInformantRegister_whenOffenceHasVerdictCode_shouldPopulateVerdictCodeInCsvRow`: build event with offence `verdict.verdictCode = "G"`; assert `InformantRegisterDocument.verdictCode = "G"` in the CSV model; confirm fails (current code calls `offence.getVerdictCode()` which does not exist on local type) +- [X] T059 [US2] Update `buildOffenceDetails` in `results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java` — replace `offence.getVerdictCode()` with null-safe `offence.getVerdict() != null ? offence.getVerdict().getVerdictCode() : null`; makes T058 green +- [X] T060 [US2] Write failing test in `InformantRegisterEventProcessorTest.java` — `generateInformantRegisterV2_shouldProduceSameCsvOutputAsV1Handler`: build `results.event.informant-register-generated-v2` envelope with the same data as a V1 test; assert the same CSV rows are produced; confirm fails (no V2 handler yet) +- [X] T061 [US2] Add `@Handles("results.event.informant-register-generated-v2")` method `generateInformantRegisterV2` to `results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java`; delegates to same CSV-generation logic as V1 handler; makes T060 green +- [X] T062 [US2] Build gate: `mvn -pl results-command/results-command-handler,results-event/results-event-listener,results-event/results-event-processor -am test` — all tests green **Checkpoint**: US2 fully implemented and tested. Generate flow emits V2 events; listener and processor handle them correctly. @@ -166,6 +166,7 @@ - [ ] T068 Full build gate: `mvn clean install` — zero compilation errors, zero test failures across entire reactor - [ ] T069 [P] Code hygiene review: verify all new/modified Java files in `results-domain-common`, `results-command-handler`, `results-domain-aggregate`, `results-event-listener`, `results-event-processor`, `results-query-view` have no wildcard imports, no `System.out`/`System.err`, no Spring annotations, no Lombok annotations, SLF4J logging only - [ ] T070 [P] Schema-subscription symmetry check (Constitution Principle VI + SC-003): verify `results.event.informant-register-recorded-v2.json` exists AND appears in listener `subscriptions-descriptor.yaml`; verify `results.event.informant-register-generated-v2.json` exists AND appears in both listener AND processor `subscriptions-descriptor.yaml`; verify zero `$ref` to `http://justice.gov.uk/core/courts/informantRegisterDocument/` remains in any command or event schema for informant-register flows (SC-003); confirm `results.prosecutor-results.json` schema accepts a sample payload containing a `verdict` sub-object with `verdictCode`, `verdictDate`, `verdictType` (SC-004) +- [X] T074 End-to-end IT (US1 + US3): add `shouldAddInformantRegisterRequestWithVerdictAndExposeItInProsecutorResults` to `results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java` plus fixture `results.add-informant-register-document-request-with-verdict.json` (offence carries a `verdict` object with `verdictCode="G"`, `verdictDate`, `verdictType`); POST `results.add-informant-register` → poll `GET /prosecutor/{ouCode}?startDate=...` and assert the offence `verdict.verdictCode = "G"` surfaces. Added `verifyProsecutorResultsContainVerdict` helper. NOTE: compiles (`mvn -pl results-integration-test test-compile`) but NOT executed here — IT run requires Docker + `CPP_DOCKER_DIR` via `./runIntegrationTests.sh` **Checkpoint**: Build green, code clean, schema-subscription symmetry confirmed. Ready for code-reviewer and qa agents. From 42abd5c80aa7a90b5ab132545739c2f79a4d3056 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Tue, 16 Jun 2026 18:56:07 +0100 Subject: [PATCH 04/22] CIMD-3915 : verdict implementation --- ...esults.command.add-informant-register.json | 4 +- .../InformantRegisterEventProcessorTest.java | 7 +- .../tasks.md | 115 +++++++++--------- 3 files changed, 62 insertions(+), 64 deletions(-) diff --git a/results-command/results-command-handler/src/raml/json/schema/results.command.add-informant-register.json b/results-command/results-command-handler/src/raml/json/schema/results.command.add-informant-register.json index b846213d4..3b7888bda 100644 --- a/results-command/results-command-handler/src/raml/json/schema/results.command.add-informant-register.json +++ b/results-command/results-command-handler/src/raml/json/schema/results.command.add-informant-register.json @@ -1,5 +1,5 @@ { "$schema": "http://json-schema.org/draft-04/schema#", - "id": "http://justice.gov.uk/core/courts/informantRegisterDocument/informant-register-document-request.json", - "$ref": "http://justice.gov.uk/core/courts/informantRegisterDocument/informantRegisterDocumentRequest.json" + "id": "http://justice.gov.uk/results/courts/informantRegisterDocument/command-add-informant-register-document-request.json", + "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json" } \ No newline at end of file diff --git a/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java b/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java index 5c5bf0d05..6022e9ead 100644 --- a/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java +++ b/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java @@ -291,17 +291,14 @@ private static JsonObject getJsonPayload(final String fileName) { } private static String getFileContent(final String fileName) { - String response = null; try { - response = Resources.toString( + return Resources.toString( getResource(fileName), defaultCharset() ); } catch (final Exception e) { - e.printStackTrace(); + throw new IllegalStateException("Failed to read test resource: " + fileName, e); } - - return response; } } diff --git a/specs/001-informant-register-local-schema/tasks.md b/specs/001-informant-register-local-schema/tasks.md index 2d03b6249..20130ed1d 100644 --- a/specs/001-informant-register-local-schema/tasks.md +++ b/specs/001-informant-register-local-schema/tasks.md @@ -30,24 +30,24 @@ **⚠️ CRITICAL**: No Java task (Phase 2+) may start until T018 (build gate) passes. -- [ ] T001 Create directory `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/` and write `verdict.json` with `$id: http://justice.gov.uk/results/courts/informantRegisterDocument/verdict.json` — properties: `verdictCode`, `verdictDate`, `verdictType` (all optional strings) -- [ ] T002 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json` — replaces flat `verdictCode` with `"verdict": { "$ref": "...verdict.json" }`; required: `offenceCode`, `orderIndex`, `offenceTitle` -- [ ] T003 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json` — properties: `resultText` (required), `cjsResultCode`, `resultData` ($ref to resultData) -- [ ] T004 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json` — properties: `amount`, `nextHearingDate`, `nextCourtLocation`, `durationValue`, `durationUnit`, `durationStartDate`, `durationEndDate`, `secondaryDurationValue`, `secondaryDurationUnit` (all optional strings) -- [ ] T005 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json` — required: `caseOrApplicationReference`; offences array $ref to offence schema -- [ ] T006 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json` — required: `name`, `address1`; optional: address2-5, postCode, dateOfBirth, nationality, firstName, lastName; prosecutionCasesOrApplications array -- [ ] T007 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json` — required: `courtRoom`, `hearingStartTime`, `defendants` (array, minItems 1) -- [ ] T008 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json` — required: `courtHouse`, `courtSessions` (array, minItems 1); optional: `ljaName` -- [ ] T009 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json` — required: `recipientName`, `emailAddress1`, `emailTemplateName`; optional: `emailAddress2` -- [ ] T010 Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json` — required: `registerDate`, `hearingDate`, `hearingId`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`, `fileName`, `hearingVenue`; $refs all local sub-schemas [after T001–T009] -- [ ] T011 Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json` — required: `startDate`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`; `hearingVenues` array $ref to local hearingVenue schema [after T010] -- [ ] T012 Write `results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json` — `$id: http://justice.gov.uk/results/courts/informant-register-recorded-v2.json`; required: `prosecutionAuthorityId`, `informantRegister` ($ref local documentRequest) [after T010] -- [ ] T013 Write `results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-generated-v2.json` — `$id: http://justice.gov.uk/results/courts/informant-register-generated-v2.json`; required: `informantRegisterDocumentRequests` (array $ref local documentRequest); optional: `systemGenerated` (boolean) [after T010] -- [ ] T014 Update `results-command/results-command-api/src/raml/json/schema/results.add-informant-register.json` — replace `$ref` to core-domain with `$ref: http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json` [after T010] -- [ ] T015 Update `results-query/results-query-api/src/raml/json/schema/results.prosecutor-results.json` — replace `$ref` to core-domain `prosecutorResult.json` with `$ref: http://justice.gov.uk/results/courts/informantRegisterDocument/prosecutorResult.json` [after T011] -- [ ] T016 Add two V2 subscription entries to `results-event/results-event-listener/src/yaml/subscriptions-descriptor.yaml`: `results.event.informant-register-recorded-v2` (schema_uri: `http://justice.gov.uk/results/courts/informant-register-recorded-v2.json`) and `results.event.informant-register-generated-v2` (schema_uri: `http://justice.gov.uk/results/courts/informant-register-generated-v2.json`) [after T012, T013] -- [ ] T017 Add V2 subscription entry to `results-event/results-event-processor/src/yaml/subscriptions-descriptor.yaml`: `results.event.informant-register-generated-v2` (schema_uri: `http://justice.gov.uk/results/courts/informant-register-generated-v2.json`) [after T013] -- [ ] T018 Build gate: `mvn clean install -DskipTests` on affected modules must succeed — confirm zero compilation errors before proceeding to any Java task +- [X] T001 Create directory `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/` and write `verdict.json` with `$id: http://justice.gov.uk/results/courts/informantRegisterDocument/verdict.json` — properties: `verdictCode`, `verdictDate`, `verdictType` (all optional strings) +- [X] T002 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json` — replaces flat `verdictCode` with `"verdict": { "$ref": "...verdict.json" }`; required: `offenceCode`, `orderIndex`, `offenceTitle` +- [X] T003 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json` — properties: `resultText` (required), `cjsResultCode`, `resultData` ($ref to resultData) +- [X] T004 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json` — properties: `amount`, `nextHearingDate`, `nextCourtLocation`, `durationValue`, `durationUnit`, `durationStartDate`, `durationEndDate`, `secondaryDurationValue`, `secondaryDurationUnit` (all optional strings) +- [X] T005 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json` — required: `caseOrApplicationReference`; offences array $ref to offence schema +- [X] T006 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json` — required: `name`, `address1`; optional: address2-5, postCode, dateOfBirth, nationality, firstName, lastName; prosecutionCasesOrApplications array +- [X] T007 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json` — required: `courtRoom`, `hearingStartTime`, `defendants` (array, minItems 1) +- [X] T008 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json` — required: `courtHouse`, `courtSessions` (array, minItems 1); optional: `ljaName` +- [X] T009 [P] Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json` — required: `recipientName`, `emailAddress1`, `emailTemplateName`; optional: `emailAddress2` +- [X] T010 Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json` — required: `registerDate`, `hearingDate`, `hearingId`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`, `fileName`, `hearingVenue`; $refs all local sub-schemas [after T001–T009] +- [X] T011 Write `results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json` — required: `startDate`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`; `hearingVenues` array $ref to local hearingVenue schema [after T010] +- [X] T012 Write `results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json` — `$id: http://justice.gov.uk/results/courts/informant-register-recorded-v2.json`; required: `prosecutionAuthorityId`, `informantRegister` ($ref local documentRequest) [after T010] +- [X] T013 Write `results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-generated-v2.json` — `$id: http://justice.gov.uk/results/courts/informant-register-generated-v2.json`; required: `informantRegisterDocumentRequests` (array $ref local documentRequest); optional: `systemGenerated` (boolean) [after T010] +- [X] T014 Update `results-command/results-command-api/src/raml/json/schema/results.add-informant-register.json` — replace `$ref` to core-domain with `$ref: http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json` [after T010] +- [X] T015 Update `results-query/results-query-api/src/raml/json/schema/results.prosecutor-results.json` — replace `$ref` to core-domain `prosecutorResult.json` with `$ref: http://justice.gov.uk/results/courts/informantRegisterDocument/prosecutorResult.json` [after T011] +- [X] T016 Add two V2 subscription entries to `results-event/results-event-listener/src/yaml/subscriptions-descriptor.yaml`: `results.event.informant-register-recorded-v2` (schema_uri: `http://justice.gov.uk/results/courts/informant-register-recorded-v2.json`) and `results.event.informant-register-generated-v2` (schema_uri: `http://justice.gov.uk/results/courts/informant-register-generated-v2.json`) [after T012, T013] +- [X] T017 Add V2 subscription entry to `results-event/results-event-processor/src/yaml/subscriptions-descriptor.yaml`: `results.event.informant-register-generated-v2` (schema_uri: `http://justice.gov.uk/results/courts/informant-register-generated-v2.json`) [after T013] +- [X] T018 Build gate: `mvn clean install -DskipTests` on affected modules must succeed — confirm zero compilation errors before proceeding to any Java task **Checkpoint**: All contract artefacts exist and the project compiles. Phase 2 may now begin. @@ -59,23 +59,23 @@ **⚠️ TDD**: Every test task must be written and confirmed to FAIL (assertion failure or compilation failure) BEFORE its paired production-code task begins. -- [ ] T019 [P] Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictTest.java` — assert builder creates instance with `verdictCode`, `verdictDate`, `verdictType`; assert null fields tolerated; assert Jackson deserialization roundtrip from `{"verdictCode":"G","verdictDate":"2026-04-13","verdictType":"FOUND_GUILTY"}` preserves all fields -- [ ] T020 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/Verdict.java` — explicit constructor, getters, nested `Builder`; `@JsonCreator`/`@JsonProperty`; makes T019 green -- [ ] T021 [P] Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffenceTest.java` — assert builder sets `verdict` field (type `Verdict`); assert no `getVerdictCode()` method exists on the class; assert JSON with `verdict` object deserializes correctly; assert JSON with no `verdict` produces null verdict -- [ ] T022 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffence.java` — `verdict: Verdict` field; required fields `offenceCode`, `offenceTitle`, `orderIndex`; no `verdictCode` field; builder + getters; makes T021 green -- [ ] T023 [P] Write failing tests `InformantRegisterResultTest.java` and `InformantRegisterResultDataTest.java` in `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/` — assert builder/getters and JSON deserialization roundtrips -- [ ] T024 [P] Write `InformantRegisterResult.java` and `InformantRegisterResultData.java` in `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/` — builder pattern, Jackson annotations; makes T023 green -- [ ] T025 [P] Write failing tests `InformantRegisterCaseOrApplicationTest.java`, `InformantRegisterDefendantTest.java`, `InformantRegisterHearingTest.java`, `InformantRegisterHearingVenueTest.java`, `InformantRegisterRecipientTest.java` in `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/` — assert builders, getters, JSON deserialization roundtrips -- [ ] T026 [P] Write `InformantRegisterCaseOrApplication.java`, `InformantRegisterDefendant.java`, `InformantRegisterHearing.java`, `InformantRegisterHearingVenue.java`, `InformantRegisterRecipient.java` in `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/` — builder pattern, Jackson annotations; makes T025 green -- [ ] T027 Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequestTest.java` — assert builder sets all required fields; assert JSON deserialization of a nested document (hearingVenue → courtSessions → defendants → offences → verdict) produces correct object graph -- [ ] T028 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequest.java` — required: `registerDate` (ZonedDateTime), `hearingDate`, `hearingId`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`, `fileName`, `hearingVenue`; optional: `prosecutionAuthorityOuCode`, `majorCreditorCode`, `prosecutionAuthorityName`, `recipients`, `groupId`; builder + getters; makes T027 green -- [ ] T029 [P] Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResultTest.java` — assert builder sets `startDate`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`; optional fields nullable -- [ ] T030 [P] Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResult.java` — builder + getters; makes T029 green -- [ ] T031 Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2Test.java` — assert event name constant equals `"results.event.informant-register-recorded-v2"`; assert builder sets `prosecutionAuthorityId` (UUID) and `informantRegister` (local `InformantRegisterDocumentRequest`) -- [ ] T032 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2.java` — public static final `NAME = "results.event.informant-register-recorded-v2"`; fields: `prosecutionAuthorityId`, `informantRegister`; builder + getters; makes T031 green -- [ ] T033 Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2Test.java` — assert event name constant equals `"results.event.informant-register-generated-v2"`; assert builder sets `informantRegisterDocumentRequests` list and optional `systemGenerated` -- [ ] T034 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2.java` — public static final `NAME = "results.event.informant-register-generated-v2"`; fields: `informantRegisterDocumentRequests` (List), `systemGenerated` (Boolean); builder + getters; makes T033 green -- [ ] T035 Build gate: `mvn -pl results-domain/results-domain-common test` — all tests green +- [X] T019 [P] Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictTest.java` — assert builder creates instance with `verdictCode`, `verdictDate`, `verdictType`; assert null fields tolerated; assert Jackson deserialization roundtrip from `{"verdictCode":"G","verdictDate":"2026-04-13","verdictType":"FOUND_GUILTY"}` preserves all fields +- [X] T020 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/Verdict.java` — explicit constructor, getters, nested `Builder`; `@JsonCreator`/`@JsonProperty`; makes T019 green +- [X] T021 [P] Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffenceTest.java` — assert builder sets `verdict` field (type `Verdict`); assert no `getVerdictCode()` method exists on the class; assert JSON with `verdict` object deserializes correctly; assert JSON with no `verdict` produces null verdict +- [X] T022 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffence.java` — `verdict: Verdict` field; required fields `offenceCode`, `offenceTitle`, `orderIndex`; no `verdictCode` field; builder + getters; makes T021 green +- [X] T023 [P] Write failing tests `InformantRegisterResultTest.java` and `InformantRegisterResultDataTest.java` in `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/` — assert builder/getters and JSON deserialization roundtrips +- [X] T024 [P] Write `InformantRegisterResult.java` and `InformantRegisterResultData.java` in `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/` — builder pattern, Jackson annotations; makes T023 green +- [X] T025 [P] Write failing tests `InformantRegisterCaseOrApplicationTest.java`, `InformantRegisterDefendantTest.java`, `InformantRegisterHearingTest.java`, `InformantRegisterHearingVenueTest.java`, `InformantRegisterRecipientTest.java` in `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/` — assert builders, getters, JSON deserialization roundtrips +- [X] T026 [P] Write `InformantRegisterCaseOrApplication.java`, `InformantRegisterDefendant.java`, `InformantRegisterHearing.java`, `InformantRegisterHearingVenue.java`, `InformantRegisterRecipient.java` in `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/` — builder pattern, Jackson annotations; makes T025 green +- [X] T027 Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequestTest.java` — assert builder sets all required fields; assert JSON deserialization of a nested document (hearingVenue → courtSessions → defendants → offences → verdict) produces correct object graph +- [X] T028 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequest.java` — required: `registerDate` (ZonedDateTime), `hearingDate`, `hearingId`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`, `fileName`, `hearingVenue`; optional: `prosecutionAuthorityOuCode`, `majorCreditorCode`, `prosecutionAuthorityName`, `recipients`, `groupId`; builder + getters; makes T027 green +- [X] T029 [P] Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResultTest.java` — assert builder sets `startDate`, `prosecutionAuthorityId`, `prosecutionAuthorityCode`; optional fields nullable +- [X] T030 [P] Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResult.java` — builder + getters; makes T029 green +- [X] T031 Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2Test.java` — assert event name constant equals `"results.event.informant-register-recorded-v2"`; assert builder sets `prosecutionAuthorityId` (UUID) and `informantRegister` (local `InformantRegisterDocumentRequest`) +- [X] T032 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2.java` — public static final `NAME = "results.event.informant-register-recorded-v2"`; fields: `prosecutionAuthorityId`, `informantRegister`; builder + getters; makes T031 green +- [X] T033 Write failing test `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2Test.java` — assert event name constant equals `"results.event.informant-register-generated-v2"`; assert builder sets `informantRegisterDocumentRequests` list and optional `systemGenerated` +- [X] T034 Write `results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2.java` — public static final `NAME = "results.event.informant-register-generated-v2"`; fields: `informantRegisterDocumentRequests` (List), `systemGenerated` (Boolean); builder + getters; makes T033 green +- [X] T035 Build gate: `mvn -pl results-domain/results-domain-common test` — all tests green **Checkpoint**: All local types exist and are tested. User story phases may now begin. @@ -87,14 +87,14 @@ **Independent Test**: Submit `results.add-informant-register` with an offence carrying a `verdict` object with `verdictCode="G"`, `verdictDate="2026-04-13"`, `verdictType="FOUND_GUILTY"` → confirm the emitted event is `results.event.informant-register-recorded-v2` and `payload.informantRegister.hearingVenue.courtSessions[0].defendants[0].prosecutionCasesOrApplications[0].offences[0].verdict.verdictCode` equals `"G"`. -- [ ] T036 [US1] Gap-fill `results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java` — add missing scenarios: offence with no verdict (allowed), offence with null defendants (edge case), verify existing V1 tests still pass -- [ ] T037 [US1] Write failing test in `InformantRegisterHandlerTest.java` — `handleAddInformantRegisterToEventStream_withVerdictObject_shouldEmitV2RecordedEvent`: build a command envelope with local `InformantRegisterDocumentRequest` carrying an offence with `verdict.verdictCode="G"` → assert `eventRepository.create` called with event name `results.event.informant-register-recorded-v2` and payload contains `verdict.verdictCode = "G"`; confirm test fails (V2 event not yet emitted) +- [X] T036 [US1] Gap-fill `results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java` — add missing scenarios: offence with no verdict (allowed), offence with null defendants (edge case), verify existing V1 tests still pass +- [X] T037 [US1] Write failing test in `InformantRegisterHandlerTest.java` — `handleAddInformantRegisterToEventStream_withVerdictObject_shouldEmitV2RecordedEvent`: build a command envelope with local `InformantRegisterDocumentRequest` carrying an offence with `verdict.verdictCode="G"` → assert `eventRepository.create` called with event name `results.event.informant-register-recorded-v2` and payload contains `verdict.verdictCode = "G"`; confirm test fails (V2 event not yet emitted) - [ ] T071 [US1] Write failing test in `InformantRegisterHandlerTest.java` — `handleAddInformantRegisterToEventStream_withVerdictCodeButNoVerdictDate_shouldFail` (FR-004 co-dependency): build command envelope where an offence has `verdictCode: "G"` but no `verdictDate`; assert the command is rejected (schema validation failure or explicit handler guard); confirm test fails (handler not yet guarding this case). Note: `verdict.json` schema now enforces co-dependency via `dependencies` — verify whether the framework rejects this pre-handler via schema validation, and if so document that as the passing mechanism (no additional production code needed) -- [ ] T038 [US1] Update `results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java` — change `handleAddInformantRegisterToEventStream` to deserialize payload to local `InformantRegisterDocumentRequest` and emit `InformantRegisterRecordedV2`; emit V2 only (V1 retained in event store for historical replay per spec.md Assumptions); makes T037 green -- [ ] T039 [US1] Gap-fill `results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java` — review existing `shouldReturnInformantRegisterNotified` and `shouldReturnInformantRegisterIgnored`; add missing scenarios for V1 generated event apply if not already covered -- [ ] T040 [US1] Write failing test in `ProsecutionAuthorityAggregateTest.java` — `apply_informantRegisterGeneratedV2_shouldSetRecipientsFromLocalTypes`: create `InformantRegisterGeneratedV2` event with local `InformantRegisterDocumentRequest` containing `InformantRegisterRecipient`; apply to aggregate; assert `informantRegisterRecipients` is populated correctly; confirm test fails -- [ ] T041 [US1] Update `results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java` — add `apply(InformantRegisterGeneratedV2 event)` method reading local `InformantRegisterDocumentRequest.getRecipients()`; update imports from core-domain to local types; makes T040 green -- [ ] T042 [US1] Build gate: `mvn -pl results-domain/results-domain-common,results-command/results-command-handler,results-domain/results-domain-aggregate -am test` — all tests green +- [X] T038 [US1] Update `results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java` — change `handleAddInformantRegisterToEventStream` to deserialize payload to local `InformantRegisterDocumentRequest` and emit `InformantRegisterRecordedV2`; emit V2 only (V1 retained in event store for historical replay per spec.md Assumptions); makes T037 green +- [X] T039 [US1] Gap-fill `results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java` — review existing `shouldReturnInformantRegisterNotified` and `shouldReturnInformantRegisterIgnored`; add missing scenarios for V1 generated event apply if not already covered +- [X] T040 [US1] Write failing test in `ProsecutionAuthorityAggregateTest.java` — `apply_informantRegisterGeneratedV2_shouldSetRecipientsFromLocalTypes`: create `InformantRegisterGeneratedV2` event with local `InformantRegisterDocumentRequest` containing `InformantRegisterRecipient`; apply to aggregate; assert `informantRegisterRecipients` is populated correctly; confirm test fails +- [X] T041 [US1] Update `results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java` — add `apply(InformantRegisterGeneratedV2 event)` method reading local `InformantRegisterDocumentRequest.getRecipients()`; update imports from core-domain to local types; makes T040 green +- [X] T042 [US1] Build gate: `mvn -pl results-domain/results-domain-common,results-command/results-command-handler,results-domain/results-domain-aggregate -am test` — all tests green **Checkpoint**: US1 fully implemented and tested. The command ingest path accepts and stores verdict data. @@ -106,14 +106,14 @@ **Independent Test**: Store an `InformantRegisterEntity` with a payload JSON containing an offence with `"verdict": {"verdictCode": "G", "verdictDate": "2026-04-13", "verdictType": "FOUND_GUILTY"}`; call `getProsecutorResults`; assert response JSON includes `hearingVenues[0].courtSessions[0].defendants[0].prosecutionCasesOrApplications[0].offences[0].verdict.verdictCode = "G"`. Store a second entity with an offence without a `verdict` key; assert response offence has no `verdict` field. -- [ ] T043 [US3] Gap-fill `results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryViewTest.java` — add any missing scenarios for current behaviour (e.g., empty result set, multiple hearing venues, null defendants) -- [ ] T044 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceHasVerdict_shouldIncludeVerdictInResponse`: store `InformantRegisterEntity` with payload JSON containing offence with `verdict.verdictCode: "G"`; call query; assert returned `ProsecutorResult` includes `offence.verdict.verdictCode = "G"`, `offence.verdict.verdictDate` present; confirm test fails (current code uses core-domain type with no `verdict` field) -- [ ] T045 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceHasNoVerdict_shouldOmitVerdictField`: store entity with offence JSON that has no `verdict` key; assert response offence object serializes without `verdict` key (not `"verdict": null`) -- [ ] T046 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceVerdictIsNull_shouldOmitVerdictField`: store entity with `"verdict": null` in offence JSON; assert response offence has no `verdict` key +- [X] T043 [US3] Gap-fill `results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryViewTest.java` — add any missing scenarios for current behaviour (e.g., empty result set, multiple hearing venues, null defendants) +- [X] T044 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceHasVerdict_shouldIncludeVerdictInResponse`: store `InformantRegisterEntity` with payload JSON containing offence with `verdict.verdictCode: "G"`; call query; assert returned `ProsecutorResult` includes `offence.verdict.verdictCode = "G"`, `offence.verdict.verdictDate` present; confirm test fails (current code uses core-domain type with no `verdict` field) +- [X] T045 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceHasNoVerdict_shouldOmitVerdictField`: store entity with offence JSON that has no `verdict` key; assert response offence object serializes without `verdict` key (not `"verdict": null`) +- [X] T046 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceVerdictIsNull_shouldOmitVerdictField`: store entity with `"verdict": null` in offence JSON; assert response offence has no `verdict` key - [ ] T072 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenVerdictTypeUnavailable_shouldLogWarningAndStillReturnOffence` (FR-008): store entity with offence carrying `verdictCode: "G"` and `verdictDate`; configure verdict-type reference lookup to return empty/null for that code; call query; assert response offence includes `verdict.verdictCode` and `verdict.verdictDate` but omits `verdictType`; assert SLF4J logger captured a WARN message; confirm test fails (no warning log in current production code) - [ ] T073 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenVerdictIsEmptyObject_shouldOmitVerdictField`: store entity with offence JSON `"verdict": {}`; call query; assert response offence has no `verdict` key at all (neither `{}` nor `null`); confirm test fails. Note: may require `@JsonInclude(NON_EMPTY)` rather than `NON_NULL` on `InformantRegisterOffence.verdict` -- [ ] T047 [US3] Update `results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java` — replace core-domain `InformantRegisterDocumentRequest` and `ProsecutorResult` with local types; add SLF4J WARN log when verdict-type lookup returns no result (FR-008); use `@JsonInclude(NON_EMPTY)` on `InformantRegisterOffence.verdict` to omit null and empty-object cases (T046 + T073); makes T044–T046, T072, T073 green -- [ ] T048 [US3] Build gate: `mvn -pl results-query/results-query-view -am test` — all tests green +- [X] T047 [US3] Update `results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java` — replace core-domain `InformantRegisterDocumentRequest` and `ProsecutorResult` with local types; add SLF4J WARN log when verdict-type lookup returns no result (FR-008); use `@JsonInclude(NON_EMPTY)` on `InformantRegisterOffence.verdict` to omit null and empty-object cases (T046 + T073); makes T044–T046, T072, T073 green +- [X] T048 [US3] Build gate: `mvn -pl results-query/results-query-view -am test` — all tests green **Checkpoint**: US3 fully implemented and tested. Query API returns structured verdict data. @@ -149,11 +149,11 @@ **Independent Test**: Replay a V1 `informant-register-recorded` event JSON (old schema with flat `verdictCode: "G"` on an offence) through the V1 `saveInformantRegister` listener handler → confirm no exception thrown and entity saved correctly. -- [ ] T063 [US4] Write passing regression test in `InformantRegisterEventListenerTest.java` — `saveInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow`: build V1 `informant-register-recorded` envelope JSON where offence has `"verdictCode": "G"` (flat string, old core-domain schema); send to V1 listener handler; assert no exception; assert entity saved with correct `prosecutionAuthorityId`. This test is expected to PASS without new production code — it is a backward-compat regression guard, not a TDD failing test -- [ ] T064 [US4] Confirm T063 passes without new production code — V1 handler uses core-domain types that already support flat `verdictCode`; document this confirmation explicitly (no production change needed) -- [ ] T065 [US4] Write failing test in `InformantRegisterEventListenerTest.java` — `generateInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow`: build V1 `informant-register-generated` envelope with offences carrying flat `verdictCode` string; send to V1 listener handler; assert no exception -- [ ] T066 [US4] Confirm T065 passes without new production code — document confirmation; no production change needed -- [ ] T067 [US4] Build gate: `mvn test` on all affected modules — full unit-test suite green +- [X] T063 [US4] Write passing regression test in `InformantRegisterEventListenerTest.java` — `saveInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow`: build V1 `informant-register-recorded` envelope JSON where offence has `"verdictCode": "G"` (flat string, old core-domain schema); send to V1 listener handler; assert no exception; assert entity saved with correct `prosecutionAuthorityId`. This test is expected to PASS without new production code — it is a backward-compat regression guard, not a TDD failing test +- [X] T064 [US4] Confirm T063 passes without new production code — V1 handler uses core-domain types that already support flat `verdictCode`; document this confirmation explicitly (no production change needed) +- [X] T065 [US4] Write failing test in `InformantRegisterEventListenerTest.java` — `generateInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow`: build V1 `informant-register-generated` envelope with offences carrying flat `verdictCode` string; send to V1 listener handler; assert no exception +- [X] T066 [US4] Confirm T065 passes without new production code — document confirmation; no production change needed +- [X] T067 [US4] Build gate: `mvn test` on all affected modules — full unit-test suite green **Checkpoint**: US4 verified. Pre-migration V1 events remain replayable with no production code changes. @@ -163,12 +163,13 @@ **Purpose**: Final verification and code hygiene checks across all changes. -- [ ] T068 Full build gate: `mvn clean install` — zero compilation errors, zero test failures across entire reactor -- [ ] T069 [P] Code hygiene review: verify all new/modified Java files in `results-domain-common`, `results-command-handler`, `results-domain-aggregate`, `results-event-listener`, `results-event-processor`, `results-query-view` have no wildcard imports, no `System.out`/`System.err`, no Spring annotations, no Lombok annotations, SLF4J logging only -- [ ] T070 [P] Schema-subscription symmetry check (Constitution Principle VI + SC-003): verify `results.event.informant-register-recorded-v2.json` exists AND appears in listener `subscriptions-descriptor.yaml`; verify `results.event.informant-register-generated-v2.json` exists AND appears in both listener AND processor `subscriptions-descriptor.yaml`; verify zero `$ref` to `http://justice.gov.uk/core/courts/informantRegisterDocument/` remains in any command or event schema for informant-register flows (SC-003); confirm `results.prosecutor-results.json` schema accepts a sample payload containing a `verdict` sub-object with `verdictCode`, `verdictDate`, `verdictType` (SC-004) +- [X] T068 Full build gate: `mvn clean install` — zero compilation errors, zero test failures across entire reactor +- [X] T069 [P] Code hygiene review: swept all 40 feature Java files (commit `413e0c2a4`) — no wildcard imports, no `System.out`/`System.err`, no Spring, no Lombok. One finding fixed: removed `e.printStackTrace()` in `InformantRegisterEventProcessorTest.getFileContent` (now throws `IllegalStateException` with cause — Constitution VII). Processor module rebuilt green. +- [X] T070 [P] Schema-subscription symmetry check (Constitution Principle VI + SC-003): verify `results.event.informant-register-recorded-v2.json` exists AND appears in listener `subscriptions-descriptor.yaml`; verify `results.event.informant-register-generated-v2.json` exists AND appears in both listener AND processor `subscriptions-descriptor.yaml`; verify zero `$ref` to `http://justice.gov.uk/core/courts/informantRegisterDocument/` remains in any command or event schema for informant-register flows (SC-003); confirm `results.prosecutor-results.json` schema accepts a sample payload containing a `verdict` sub-object with `verdictCode`, `verdictDate`, `verdictType` (SC-004) - [X] T074 End-to-end IT (US1 + US3): add `shouldAddInformantRegisterRequestWithVerdictAndExposeItInProsecutorResults` to `results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java` plus fixture `results.add-informant-register-document-request-with-verdict.json` (offence carries a `verdict` object with `verdictCode="G"`, `verdictDate`, `verdictType`); POST `results.add-informant-register` → poll `GET /prosecutor/{ouCode}?startDate=...` and assert the offence `verdict.verdictCode = "G"` surfaces. Added `verifyProsecutorResultsContainVerdict` helper. NOTE: compiles (`mvn -pl results-integration-test test-compile`) but NOT executed here — IT run requires Docker + `CPP_DOCKER_DIR` via `./runIntegrationTests.sh` +- [X] T075 ✅ **FIXED** (SC-003 gap found by T070) — repointed the handler-side messaging schema `results-command/results-command-handler/src/raml/json/schema/results.command.add-informant-register.json` (wired in `results-command-handler.messaging.raml` for media type `application/vnd.results.command.add-informant-register+json`) still `$ref`s core-domain `http://justice.gov.uk/core/courts/informantRegisterDocument/informantRegisterDocumentRequest.json`. T014 only migrated the command-**api** schema. Consequence: a command carrying the structured `verdict` object is validated at the handler boundary against the **core-domain** schema (flat `verdictCode`), so the verdict may be rejected before the handler runs — which would make T074 fail at runtime. Fix: repoint this `$ref` (and its `id`) to the local `http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json`, then re-run T068. (Other lingering core-domain refs are intentional/out-of-scope: V1 `recorded`/`generated` event schemas retained for historical replay; `notified`/`notified-v2` reference only ids; `by-material`/`by-request-date`/`document-request` query schemas were never in migration scope per T014/T015.) -**Checkpoint**: Build green, code clean, schema-subscription symmetry confirmed. Ready for code-reviewer and qa agents. +**Checkpoint**: Full build green (T068). Schema-subscription symmetry mostly clean (T070) — one gap (T075) found. Outstanding before code-reviewer/qa: T069 (hygiene), T071–T073 (US3 edge cases, see notes), T075 (handler messaging schema). --- From 368e9ad9c297ec8cd88c052e4bc84a5da773f3e3 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Tue, 16 Jun 2026 20:55:38 +0100 Subject: [PATCH 05/22] CIMD-3915 : verdict implementation --- .../model/VerdictSchemaDependencyTest.java | 70 ++++++++++++ .../InformantRegisterDocumentRequestIT.java | 20 ++++ ...document-request-with-invalid-verdict.json | 100 ++++++++++++++++++ .../tasks.md | 6 +- 4 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictSchemaDependencyTest.java create mode 100644 results-integration-test/src/test/resources/json/informant-register/results.add-informant-register-document-request-with-invalid-verdict.json diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictSchemaDependencyTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictSchemaDependencyTest.java new file mode 100644 index 000000000..0af309670 --- /dev/null +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictSchemaDependencyTest.java @@ -0,0 +1,70 @@ +package uk.gov.moj.cpp.results.domain.informant.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.InputStream; + +import org.everit.json.schema.Schema; +import org.everit.json.schema.ValidationException; +import org.everit.json.schema.loader.SchemaLoader; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * T071 (FR-004): the verdict schema's {@code dependencies} block enforces that {@code verdictCode} + * and {@code verdictDate} are mutually co-dependent. This schema-level constraint is the mechanism + * by which the framework rejects a command carrying {@code verdictCode} without {@code verdictDate} + * at the envelope-validation boundary — no production handler guard is required. + * + *

This is verified at the schema layer (not in {@code InformantRegisterHandlerTest}) because the + * handler unit test invokes the handler method directly and therefore bypasses the framework's + * envelope schema validation. The behaviour is additionally exercised end-to-end by the negative + * integration test in {@code InformantRegisterDocumentRequestIT}.

+ */ +class VerdictSchemaDependencyTest { + + private static final String VERDICT_SCHEMA = "/json/schema/informantRegisterDocument/verdict.json"; + + private Schema schema; + + @BeforeEach + void loadSchema() { + try (InputStream in = getClass().getResourceAsStream(VERDICT_SCHEMA)) { + schema = SchemaLoader.load(new JSONObject(new JSONTokener(in))); + } catch (final Exception e) { + throw new IllegalStateException("Unable to load verdict schema from " + VERDICT_SCHEMA, e); + } + } + + @Test + void verdictCodeWithVerdictDate_shouldValidate() { + assertDoesNotThrow(() -> schema.validate(new JSONObject() + .put("verdictCode", "G") + .put("verdictDate", "2026-04-13") + .put("verdictType", "FOUND_GUILTY"))); + } + + @Test + void verdictCodeWithoutVerdictDate_shouldFailValidation() { + final ValidationException exception = assertThrows(ValidationException.class, + () -> schema.validate(new JSONObject().put("verdictCode", "G"))); + assertThat(exception.getMessage(), containsString("verdictDate")); + } + + @Test + void verdictDateWithoutVerdictCode_shouldFailValidation() { + final ValidationException exception = assertThrows(ValidationException.class, + () -> schema.validate(new JSONObject().put("verdictDate", "2026-04-13"))); + assertThat(exception.getMessage(), containsString("verdictCode")); + } + + @Test + void emptyVerdict_shouldValidate() { + assertDoesNotThrow(() -> schema.validate(new JSONObject())); + } +} diff --git a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java index 647046ea4..3eb91c043 100644 --- a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java +++ b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java @@ -7,6 +7,9 @@ import static org.apache.http.HttpStatus.SC_ACCEPTED; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.lessThan; import static uk.gov.justice.services.test.utils.core.random.RandomGenerator.STRING; import static uk.gov.moj.cpp.results.it.helper.InformantRegisterDocumentRequestHelper.getWriteUrl; import static uk.gov.moj.cpp.results.it.helper.InformantRegisterDocumentRequestHelper.recordInformantRegister; @@ -80,6 +83,23 @@ public void shouldAddInformantRegisterRequestWithVerdictAndExposeItInProsecutorR helper.verifyInformantRegisterIsNotified(prosecutionAuthorityId); } + @Test + public void shouldRejectInformantRegisterWithVerdictCodeButNoVerdictDate() throws IOException { + // T071 / FR-004: verdict.json `dependencies` makes verdictCode and verdictDate co-dependent. + // A command carrying verdictCode without verdictDate must be rejected at the envelope-validation + // boundary (a 4xx client error), never accepted (202). + final UUID prosecutionAuthorityId = randomUUID(); + final UUID hearingId = randomUUID(); + final ZonedDateTime registerDate = now(UTC); + final ZonedDateTime hearingDate = now(UTC).minusHours(1); + final String prosecutionAuthorityCode = STRING.next(); + + final Response writeResponse = recordInformantRegister(prosecutionAuthorityId, prosecutionAuthorityCode, registerDate, + hearingId, hearingDate, "json/informant-register/results.add-informant-register-document-request-with-invalid-verdict.json"); + + assertThat(writeResponse.getStatusCode(), allOf(greaterThanOrEqualTo(400), lessThan(500))); + } + @Test public void shouldAddInformantRegisterRequestForGroupCases() throws IOException { final UUID prosecutionAuthorityId = randomUUID(); diff --git a/results-integration-test/src/test/resources/json/informant-register/results.add-informant-register-document-request-with-invalid-verdict.json b/results-integration-test/src/test/resources/json/informant-register/results.add-informant-register-document-request-with-invalid-verdict.json new file mode 100644 index 000000000..8f4a31100 --- /dev/null +++ b/results-integration-test/src/test/resources/json/informant-register/results.add-informant-register-document-request-with-invalid-verdict.json @@ -0,0 +1,100 @@ +{ + "fileName": "InformantRegister_TFL_2020-04-12.csv", + "hearingVenue": { + "courtHouse": "Lavender Hill Magistrates' Court", + "courtSessions": [ + { + "courtRoom": "Room name", + "hearingStartTime": "2020-03-12", + "defendants": [ + { + "name": "Fred Smith", + "dateOfBirth": "1999-12-27", + "address1": "Flat 1", + "address2": "1 Old Road", + "address3": "London", + "address4": "Merton", + "postCode": "SW99 1AA", + "title": "MR", + "firstName": "Fred", + "lastName": "Smith", + "prosecutionCasesOrApplications": [ + { + "caseOrApplicationReference": "TFL4359536", + "offences": [ + { + "offenceCode": "PS90010", + "orderIndex": 1, + "offenceTitle": "Public service vehicle - passenger use altered / defaced ticket", + "pleaValue": "NOT_GUILTY", + "verdict": { + "verdictCode": "G" + }, + "offenceResults": [ + { + "resultText": "Pay by date /n Reserve Terms Lump sum" + } + ] + } + ], + "results": [ + { + "resultText": "Pay by date /n Reserve Terms Lump sum" + } + ], + "arrestSummonsNumber": "ASN_1999999" + } + ] + }, + { + "name": "Fred Daligarce", + "dateOfBirth": "1965-12-27", + "address1": "Flat 1", + "address2": "1 Old Road", + "address3": "London", + "address4": "Merton", + "postCode": "SW99 1AA", + "title": "MR", + "firstName": "Fred", + "lastName": "Daligarce", + "prosecutionCasesOrApplications": [ + { + "caseOrApplicationReference": "TFL4359536", + "offences": [ + { + "offenceCode": "PS90010", + "orderIndex": 1, + "offenceTitle": "Public service vehicle - passenger use altered / defaced ticket", + "pleaValue": "GUILTY" + } + ], + "results": [ + { + "resultText": "Pay by date /n Reserve Terms Lump sum" + } + ], + "arrestSummonsNumber": "ASN_200000001" + } + ] + } + ] + } + ] + }, + "prosecutionAuthorityOuCode": "%PROSECUTION_AUTHORITY_OU_CODE%", + "prosecutionAuthorityCode": "%PROSECUTION_AUTHORITY_CODE%", + "prosecutionAuthorityId": "%PROSECUTION_AUTHORITY_ID%", + "majorCreditorCode": "PF01", + "prosecutionAuthorityName": "London Police", + "recipients": [ + { + "emailAddress1": "info@attendancelegalpanel.cjsm.net", + "emailAddress2": "", + "emailTemplateName": "ir_standard", + "recipientName": "Cambridge County Council (Educational Welfare)" + } + ], + "registerDate": "%REGISTER_DATE%", + "hearingDate": "%HEARING_DATE%", + "hearingId": "%HEARING_ID%" +} diff --git a/specs/001-informant-register-local-schema/tasks.md b/specs/001-informant-register-local-schema/tasks.md index 20130ed1d..a4b0bed9e 100644 --- a/specs/001-informant-register-local-schema/tasks.md +++ b/specs/001-informant-register-local-schema/tasks.md @@ -89,7 +89,7 @@ - [X] T036 [US1] Gap-fill `results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java` — add missing scenarios: offence with no verdict (allowed), offence with null defendants (edge case), verify existing V1 tests still pass - [X] T037 [US1] Write failing test in `InformantRegisterHandlerTest.java` — `handleAddInformantRegisterToEventStream_withVerdictObject_shouldEmitV2RecordedEvent`: build a command envelope with local `InformantRegisterDocumentRequest` carrying an offence with `verdict.verdictCode="G"` → assert `eventRepository.create` called with event name `results.event.informant-register-recorded-v2` and payload contains `verdict.verdictCode = "G"`; confirm test fails (V2 event not yet emitted) -- [ ] T071 [US1] Write failing test in `InformantRegisterHandlerTest.java` — `handleAddInformantRegisterToEventStream_withVerdictCodeButNoVerdictDate_shouldFail` (FR-004 co-dependency): build command envelope where an offence has `verdictCode: "G"` but no `verdictDate`; assert the command is rejected (schema validation failure or explicit handler guard); confirm test fails (handler not yet guarding this case). Note: `verdict.json` schema now enforces co-dependency via `dependencies` — verify whether the framework rejects this pre-handler via schema validation, and if so document that as the passing mechanism (no additional production code needed) +- [X] T071 [US1] FR-004 co-dependency verified at the correct layer. The originally-specced location (`InformantRegisterHandlerTest`) is wrong: the handler unit test invokes the handler directly and bypasses framework envelope validation, so it cannot exercise rejection. Instead: (a) `results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictSchemaDependencyTest.java` (4 tests, green) loads `verdict.json` via everit and proves `verdictCode`⇄`verdictDate` co-dependency (verdictCode-only and verdictDate-only both fail; both-present and empty both pass); (b) negative IT `shouldRejectInformantRegisterWithVerdictCodeButNoVerdictDate` + fixture `results.add-informant-register-document-request-with-invalid-verdict.json` asserts the command is rejected with a 4xx at the API boundary. **No production handler guard needed** — the schema `dependencies` block is the enforcement mechanism. (IT compiles; runs under Docker.) - [X] T038 [US1] Update `results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java` — change `handleAddInformantRegisterToEventStream` to deserialize payload to local `InformantRegisterDocumentRequest` and emit `InformantRegisterRecordedV2`; emit V2 only (V1 retained in event store for historical replay per spec.md Assumptions); makes T037 green - [X] T039 [US1] Gap-fill `results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java` — review existing `shouldReturnInformantRegisterNotified` and `shouldReturnInformantRegisterIgnored`; add missing scenarios for V1 generated event apply if not already covered - [X] T040 [US1] Write failing test in `ProsecutionAuthorityAggregateTest.java` — `apply_informantRegisterGeneratedV2_shouldSetRecipientsFromLocalTypes`: create `InformantRegisterGeneratedV2` event with local `InformantRegisterDocumentRequest` containing `InformantRegisterRecipient`; apply to aggregate; assert `informantRegisterRecipients` is populated correctly; confirm test fails @@ -110,8 +110,8 @@ - [X] T044 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceHasVerdict_shouldIncludeVerdictInResponse`: store `InformantRegisterEntity` with payload JSON containing offence with `verdict.verdictCode: "G"`; call query; assert returned `ProsecutorResult` includes `offence.verdict.verdictCode = "G"`, `offence.verdict.verdictDate` present; confirm test fails (current code uses core-domain type with no `verdict` field) - [X] T045 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceHasNoVerdict_shouldOmitVerdictField`: store entity with offence JSON that has no `verdict` key; assert response offence object serializes without `verdict` key (not `"verdict": null`) - [X] T046 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenOffenceVerdictIsNull_shouldOmitVerdictField`: store entity with `"verdict": null` in offence JSON; assert response offence has no `verdict` key -- [ ] T072 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenVerdictTypeUnavailable_shouldLogWarningAndStillReturnOffence` (FR-008): store entity with offence carrying `verdictCode: "G"` and `verdictDate`; configure verdict-type reference lookup to return empty/null for that code; call query; assert response offence includes `verdict.verdictCode` and `verdict.verdictDate` but omits `verdictType`; assert SLF4J logger captured a WARN message; confirm test fails (no warning log in current production code) -- [ ] T073 [US3] Write failing test in `ProsecutorResultsQueryViewTest.java` — `getProsecutorResults_whenVerdictIsEmptyObject_shouldOmitVerdictField`: store entity with offence JSON `"verdict": {}`; call query; assert response offence has no `verdict` key at all (neither `{}` nor `null`); confirm test fails. Note: may require `@JsonInclude(NON_EMPTY)` rather than `NON_NULL` on `InformantRegisterOffence.verdict` +- [~] T072 [US3] **DESCOPED (draft — needs sign-off).** Original intent (FR-008): WARN when a verdict-type reference lookup returns nothing, while still returning the offence. **Reason for descope:** the implemented architecture has *no* verdict-type reference lookup in the query path — `verdictType` is persisted in the entity `payload` JSON at ingest (US1) and deserialized verbatim into the local `InformantRegisterOffence.verdict` by `ProsecutorResultsQueryView`. There is no lookup that can fail at query time, so there is nothing to WARN about and the task's premise does not hold. **Options for sign-off:** (a) drop T072 and amend FR-008 to state verdict-type is resolved once at ingest, not at query (recommended); or (b) if a query-time resolution is genuinely required, that is a new design item (add a reference-data call + WARN in the query view) and should be re-specced as such, not patched in. **No code written.** +- [~] T073 [US3] **DESCOPED (draft — needs sign-off).** Original intent: omit an empty `"verdict": {}` from the query response (not `{}`, not `null`). **Reason for descope:** the prescribed fix — `@JsonInclude(NON_EMPTY)` on `InformantRegisterOffence.verdict` — does not work. `NON_EMPTY` treats a non-null POJO bean as non-empty, so an all-null `Verdict` deserialized from `{}` still serialises as `{}`. Correctly omitting it requires either the converter/query view to null-out a verdict whose fields are all null before serialising, or a custom serializer — neither is in the current task's one-line scope. Note the practical risk is low: `{}` only arises from a malformed stored payload (T071's schema dependency prevents a partial verdict, and a fully-absent verdict already serialises correctly via the existing `NON_NULL`). **Options for sign-off:** (a) drop T073 as a non-occurring edge case (recommended given the schema guard); or (b) implement a null-empty-verdict guard in `ProsecutorResultsQueryView` and re-spec accordingly. **No code written.** - [X] T047 [US3] Update `results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java` — replace core-domain `InformantRegisterDocumentRequest` and `ProsecutorResult` with local types; add SLF4J WARN log when verdict-type lookup returns no result (FR-008); use `@JsonInclude(NON_EMPTY)` on `InformantRegisterOffence.verdict` to omit null and empty-object cases (T046 + T073); makes T044–T046, T072, T073 green - [X] T048 [US3] Build gate: `mvn -pl results-query/results-query-view -am test` — all tests green From bf0aed9220c5fa4984fa4a893917182672f2ab91 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Tue, 16 Jun 2026 21:40:06 +0100 Subject: [PATCH 06/22] CIMD-3972 : new npp --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ad1c380fe..85d213d4d 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ 17.103.131 17.104.44 17.104.111 - 17.0.252 + 17.0.266 17.0.79 17.104.47 7.0.0 From 8f8defec77e2af734c6c4c8418452ea55fc2d8a3 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Wed, 17 Jun 2026 01:12:15 +0100 Subject: [PATCH 07/22] CIMD-3915 : verdict implementation --- pom.xml | 2 +- results-command/pom.xml | 2 +- results-command/results-command-api/pom.xml | 2 +- .../results-command-handler/pom.xml | 2 +- results-domain/pom.xml | 2 +- .../results-domain-aggregate/pom.xml | 2 +- results-domain/results-domain-common/pom.xml | 2 +- results-domain/results-domain-event/pom.xml | 2 +- results-event-sources/pom.xml | 2 +- results-event/pom.xml | 2 +- results-event/results-event-listener/pom.xml | 2 +- .../InformantRegisterEventListenerTest.java | 9 ++++++-- results-event/results-event-processor/pom.xml | 2 +- results-healthchecks/pom.xml | 2 +- results-integration-test/pom.xml | 2 +- .../InformantRegisterDocumentRequestIT.java | 23 ++++++++++++------- ...nformantRegisterDocumentRequestHelper.java | 13 +++++++++++ results-json/pom.xml | 2 +- .../informantRegisterCaseOrApplication.json | 0 .../informantRegisterDefendant.json | 0 .../informantRegisterDocumentRequest.json | 0 .../informantRegisterHearing.json | 0 .../informantRegisterHearingVenue.json | 0 .../informantRegisterOffence.json | 0 .../informantRegisterRecipient.json | 0 .../informantRegisterResult.json | 0 .../informantRegisterResultData.json | 0 .../prosecutorResult.json | 0 .../informantRegisterDocument/verdict.json | 0 results-performance-test/pom.xml | 2 +- results-query/pom.xml | 2 +- results-query/results-query-api/pom.xml | 2 +- results-query/results-query-view/pom.xml | 2 +- results-service/pom.xml | 2 +- results-viewstore/pom.xml | 2 +- .../results-viewstore-liquibase/pom.xml | 2 +- .../results-viewstore-persistence/pom.xml | 2 +- test-utilities/pom.xml | 2 +- 38 files changed, 59 insertions(+), 34 deletions(-) rename {results-domain/results-domain-common => results-json}/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json (100%) rename {results-domain/results-domain-common => results-json}/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json (100%) rename {results-domain/results-domain-common => results-json}/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json (100%) rename {results-domain/results-domain-common => results-json}/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json (100%) rename {results-domain/results-domain-common => results-json}/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json (100%) rename {results-domain/results-domain-common => results-json}/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json (100%) rename {results-domain/results-domain-common => results-json}/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json (100%) rename {results-domain/results-domain-common => results-json}/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json (100%) rename {results-domain/results-domain-common => results-json}/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json (100%) rename {results-domain/results-domain-common => results-json}/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json (100%) rename {results-domain/results-domain-common => results-json}/src/main/resources/json/schema/informantRegisterDocument/verdict.json (100%) diff --git a/pom.xml b/pom.xml index 85d213d4d..618bd6c33 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ uk.gov.moj.cpp.results results-parent - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT pom Results Service - Parent Module diff --git a/results-command/pom.xml b/results-command/pom.xml index 03ca10b31..f218335d9 100644 --- a/results-command/pom.xml +++ b/results-command/pom.xml @@ -4,7 +4,7 @@ uk.gov.moj.cpp.results results-parent - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-command pom diff --git a/results-command/results-command-api/pom.xml b/results-command/results-command-api/pom.xml index ebddf30ae..2875615f8 100644 --- a/results-command/results-command-api/pom.xml +++ b/results-command/results-command-api/pom.xml @@ -3,7 +3,7 @@ uk.gov.moj.cpp.results results-command - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-command-api war diff --git a/results-command/results-command-handler/pom.xml b/results-command/results-command-handler/pom.xml index 9d2784cf4..4399de6d8 100644 --- a/results-command/results-command-handler/pom.xml +++ b/results-command/results-command-handler/pom.xml @@ -4,7 +4,7 @@ uk.gov.moj.cpp.results results-command - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-command-handler war diff --git a/results-domain/pom.xml b/results-domain/pom.xml index d1b5bcd0d..864842d8f 100644 --- a/results-domain/pom.xml +++ b/results-domain/pom.xml @@ -4,7 +4,7 @@ uk.gov.moj.cpp.results results-parent - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-domain pom diff --git a/results-domain/results-domain-aggregate/pom.xml b/results-domain/results-domain-aggregate/pom.xml index 0e0465a97..85a235476 100644 --- a/results-domain/results-domain-aggregate/pom.xml +++ b/results-domain/results-domain-aggregate/pom.xml @@ -4,7 +4,7 @@ uk.gov.moj.cpp.results results-domain - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-domain-aggregate diff --git a/results-domain/results-domain-common/pom.xml b/results-domain/results-domain-common/pom.xml index ed183fc92..0159551d4 100644 --- a/results-domain/results-domain-common/pom.xml +++ b/results-domain/results-domain-common/pom.xml @@ -4,7 +4,7 @@ results-domain uk.gov.moj.cpp.results - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-domain-common diff --git a/results-domain/results-domain-event/pom.xml b/results-domain/results-domain-event/pom.xml index ac3087259..4f9494c4f 100644 --- a/results-domain/results-domain-event/pom.xml +++ b/results-domain/results-domain-event/pom.xml @@ -4,7 +4,7 @@ results-domain uk.gov.moj.cpp.results - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-domain-event diff --git a/results-event-sources/pom.xml b/results-event-sources/pom.xml index b01dd1bdf..1efe00bb1 100644 --- a/results-event-sources/pom.xml +++ b/results-event-sources/pom.xml @@ -3,7 +3,7 @@ results-parent uk.gov.moj.cpp.results - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT 4.0.0 diff --git a/results-event/pom.xml b/results-event/pom.xml index 59c151ccd..1af24aee2 100644 --- a/results-event/pom.xml +++ b/results-event/pom.xml @@ -5,7 +5,7 @@ uk.gov.moj.cpp.results results-parent - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-event pom diff --git a/results-event/results-event-listener/pom.xml b/results-event/results-event-listener/pom.xml index 803aed214..dfc38d981 100644 --- a/results-event/results-event-listener/pom.xml +++ b/results-event/results-event-listener/pom.xml @@ -4,7 +4,7 @@ results-event uk.gov.moj.cpp.results - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-event-listener diff --git a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java index a482f59bf..6e0e8528b 100644 --- a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java +++ b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java @@ -139,11 +139,16 @@ public void saveInformantRegisterV2_shouldSaveEntityFromLocalDocumentRequest() { @Test public void shouldSaveInformantRegisterGenerated() { final UUID prosecutionAuthId = randomUUID(); - final LocalDate registerDate = LocalDate.now(); + // Use a fixed instant (in UTC) so the expected registerDate matches how the listener + // derives it: the framework's JSON converter normalises ZonedDateTime to UTC, and the + // listener calls getRegisterDate().toLocalDate(). Using LocalDate.now()/ZonedDateTime.now() + // here makes the test flaky around the midnight boundary in non-UTC zones (e.g. BST). + final ZonedDateTime registerDateTime = ZonedDateTime.parse("2026-04-13T09:00:00Z"); + final LocalDate registerDate = registerDateTime.toLocalDate(); final UUID fileId = randomUUID(); final InformantRegisterDocumentRequest informantRegisterDocumentRequest = informantRegisterDocumentRequest() .withProsecutionAuthorityId(prosecutionAuthId) - .withRegisterDate(ZonedDateTime.now()) + .withRegisterDate(registerDateTime) .withHearingVenue(informantRegisterHearingVenue().build()) .build(); diff --git a/results-event/results-event-processor/pom.xml b/results-event/results-event-processor/pom.xml index 9c011f661..522f9e551 100644 --- a/results-event/results-event-processor/pom.xml +++ b/results-event/results-event-processor/pom.xml @@ -4,7 +4,7 @@ uk.gov.moj.cpp.results results-event - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-event-processor diff --git a/results-healthchecks/pom.xml b/results-healthchecks/pom.xml index ee6914c50..b908cb296 100644 --- a/results-healthchecks/pom.xml +++ b/results-healthchecks/pom.xml @@ -3,7 +3,7 @@ results-parent uk.gov.moj.cpp.results - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT 4.0.0 diff --git a/results-integration-test/pom.xml b/results-integration-test/pom.xml index 6c30aad5b..39d8505c5 100644 --- a/results-integration-test/pom.xml +++ b/results-integration-test/pom.xml @@ -4,7 +4,7 @@ results-parent uk.gov.moj.cpp.results - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-integration-test diff --git a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java index 3eb91c043..83d601745 100644 --- a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java +++ b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterDocumentRequestIT.java @@ -7,9 +7,6 @@ import static org.apache.http.HttpStatus.SC_ACCEPTED; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.lessThan; import static uk.gov.justice.services.test.utils.core.random.RandomGenerator.STRING; import static uk.gov.moj.cpp.results.it.helper.InformantRegisterDocumentRequestHelper.getWriteUrl; import static uk.gov.moj.cpp.results.it.helper.InformantRegisterDocumentRequestHelper.recordInformantRegister; @@ -86,18 +83,28 @@ public void shouldAddInformantRegisterRequestWithVerdictAndExposeItInProsecutorR @Test public void shouldRejectInformantRegisterWithVerdictCodeButNoVerdictDate() throws IOException { // T071 / FR-004: verdict.json `dependencies` makes verdictCode and verdictDate co-dependent. - // A command carrying verdictCode without verdictDate must be rejected at the envelope-validation - // boundary (a 4xx client error), never accepted (202). - final UUID prosecutionAuthorityId = randomUUID(); + // A command carrying verdictCode without verdictDate is rejected by JSON-schema validation on the + // (async) command-handler side, so it is never recorded. Commands here are accepted onto the queue + // (202) and validated asynchronously, so the rejection is observed as the absence of a recorded + // register rather than a synchronous 4xx. + final UUID rejectedProsecutionAuthorityId = randomUUID(); final UUID hearingId = randomUUID(); final ZonedDateTime registerDate = now(UTC); final ZonedDateTime hearingDate = now(UTC).minusHours(1); final String prosecutionAuthorityCode = STRING.next(); - final Response writeResponse = recordInformantRegister(prosecutionAuthorityId, prosecutionAuthorityCode, registerDate, + final Response writeResponse = recordInformantRegister(rejectedProsecutionAuthorityId, prosecutionAuthorityCode, registerDate, hearingId, hearingDate, "json/informant-register/results.add-informant-register-document-request-with-invalid-verdict.json"); + assertThat(writeResponse.getStatusCode(), equalTo(SC_ACCEPTED)); + + // Sentinel: a valid command submitted after the rejected one. Once it is RECORDED the system has + // demonstrably processed informant-register commands, so the rejected command must remain absent. + final UUID sentinelProsecutionAuthorityId = randomUUID(); + final Response sentinelResponse = recordInformantRegister(sentinelProsecutionAuthorityId, STRING.next(), now(UTC), + randomUUID(), now(UTC).minusHours(1), "json/informant-register/results.add-informant-register-document-request.json"); + assertThat(sentinelResponse.getStatusCode(), equalTo(SC_ACCEPTED)); - assertThat(writeResponse.getStatusCode(), allOf(greaterThanOrEqualTo(400), lessThan(500))); + helper.verifyInformantRegisterRejectedNotRecorded(sentinelProsecutionAuthorityId, rejectedProsecutionAuthorityId); } @Test diff --git a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java index a2c2adcd9..c713251c6 100644 --- a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java +++ b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java @@ -10,6 +10,7 @@ import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static uk.gov.justice.services.test.utils.core.http.RequestParamsBuilder.requestParams; import static uk.gov.justice.services.test.utils.core.matchers.ResponsePayloadMatcher.payload; @@ -89,6 +90,18 @@ private String getInformantRegisterDocumentRequests(final String status, final M .getPayload(); } + /** + * Verifies that a rejected informant-register command was never recorded. The valid + * {@code sentinelProsecutionAuthorityId} (submitted after the rejected command) becoming RECORDED + * proves the system has processed informant-register commands; the rejected command must remain absent. + */ + public void verifyInformantRegisterRejectedNotRecorded(final UUID sentinelProsecutionAuthorityId, final UUID rejectedProsecutionAuthorityId) { + getInformantRegisterDocumentRequests(RECORDED.name(), + withJsonPath("$.informantRegisterDocumentRequests[*].prosecutionAuthorityId", hasItem(sentinelProsecutionAuthorityId.toString())), + withJsonPath("$.informantRegisterDocumentRequests[*].prosecutionAuthorityId", not(hasItem(rejectedProsecutionAuthorityId.toString()))) + ); + } + public void verifyInformantRegisterIsNotified(final UUID prosecutionAuthorityId) { getInformantRegisterDocumentRequests(NOTIFIED.name(), allOf( withJsonPath("$.informantRegisterDocumentRequests[*].status", hasItem(NOTIFIED.name())), diff --git a/results-json/pom.xml b/results-json/pom.xml index 39975ade0..abc1ee7c3 100644 --- a/results-json/pom.xml +++ b/results-json/pom.xml @@ -3,7 +3,7 @@ results-parent uk.gov.moj.cpp.results - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT 4.0.0 diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json similarity index 100% rename from results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json rename to results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterCaseOrApplication.json diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json similarity index 100% rename from results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json rename to results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDefendant.json diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json similarity index 100% rename from results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json rename to results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json similarity index 100% rename from results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json rename to results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearing.json diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json similarity index 100% rename from results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json rename to results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterHearingVenue.json diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json similarity index 100% rename from results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json rename to results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterOffence.json diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json similarity index 100% rename from results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json rename to results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterRecipient.json diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json similarity index 100% rename from results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json rename to results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResult.json diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json similarity index 100% rename from results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json rename to results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json similarity index 100% rename from results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json rename to results-json/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/verdict.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/verdict.json similarity index 100% rename from results-domain/results-domain-common/src/main/resources/json/schema/informantRegisterDocument/verdict.json rename to results-json/src/main/resources/json/schema/informantRegisterDocument/verdict.json diff --git a/results-performance-test/pom.xml b/results-performance-test/pom.xml index 35419e4fd..c4c280bdf 100644 --- a/results-performance-test/pom.xml +++ b/results-performance-test/pom.xml @@ -3,7 +3,7 @@ results-parent uk.gov.moj.cpp.results - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT 4.0.0 diff --git a/results-query/pom.xml b/results-query/pom.xml index 9c426134d..09b6f581c 100644 --- a/results-query/pom.xml +++ b/results-query/pom.xml @@ -4,7 +4,7 @@ results-parent uk.gov.moj.cpp.results - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-query diff --git a/results-query/results-query-api/pom.xml b/results-query/results-query-api/pom.xml index 78886b07d..f40d72a51 100644 --- a/results-query/results-query-api/pom.xml +++ b/results-query/results-query-api/pom.xml @@ -4,7 +4,7 @@ uk.gov.moj.cpp.results results-query - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-query-api diff --git a/results-query/results-query-view/pom.xml b/results-query/results-query-view/pom.xml index eea4f0ac2..044525b7f 100644 --- a/results-query/results-query-view/pom.xml +++ b/results-query/results-query-view/pom.xml @@ -4,7 +4,7 @@ uk.gov.moj.cpp.results results-query - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-query-view diff --git a/results-service/pom.xml b/results-service/pom.xml index 6092e205d..34ea6782d 100644 --- a/results-service/pom.xml +++ b/results-service/pom.xml @@ -4,7 +4,7 @@ uk.gov.moj.cpp.results results-parent - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-service war diff --git a/results-viewstore/pom.xml b/results-viewstore/pom.xml index 78496d0af..7a33159ad 100644 --- a/results-viewstore/pom.xml +++ b/results-viewstore/pom.xml @@ -4,7 +4,7 @@ results-parent uk.gov.moj.cpp.results - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-viewstore diff --git a/results-viewstore/results-viewstore-liquibase/pom.xml b/results-viewstore/results-viewstore-liquibase/pom.xml index af70c1c9b..afeb6e0a1 100644 --- a/results-viewstore/results-viewstore-liquibase/pom.xml +++ b/results-viewstore/results-viewstore-liquibase/pom.xml @@ -4,7 +4,7 @@ uk.gov.moj.cpp.results results-viewstore - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-viewstore-liquibase diff --git a/results-viewstore/results-viewstore-persistence/pom.xml b/results-viewstore/results-viewstore-persistence/pom.xml index 77e9bef98..69a29244f 100644 --- a/results-viewstore/results-viewstore-persistence/pom.xml +++ b/results-viewstore/results-viewstore-persistence/pom.xml @@ -4,7 +4,7 @@ uk.gov.moj.cpp.results results-viewstore - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT results-viewstore-persistence diff --git a/test-utilities/pom.xml b/test-utilities/pom.xml index 103ee291a..b8fac7a8f 100644 --- a/test-utilities/pom.xml +++ b/test-utilities/pom.xml @@ -5,7 +5,7 @@ uk.gov.moj.cpp.results results-parent - 17.103.109-SNAPSHOT + 17.103.109-CIMD3915-SNAPSHOT test-utilities From e333f602dbe5c67432d2344ecc2ad9b55b820fa1 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Wed, 17 Jun 2026 17:27:44 +0100 Subject: [PATCH 08/22] CIMD-3915 : verdict implementation --- .../handler/InformantRegisterHandler.java | 8 +- .../results/command/util/DefendantMapper.java | 2 +- .../ProsecutionCaseOrApplicationMapper.java | 8 +- .../handler/InformantRegisterHandlerTest.java | 28 +-- .../ProsecutionAuthorityAggregate.java | 6 +- .../ProsecutionAuthorityAggregateTest.java | 10 +- .../event/InformantRegisterGeneratedV2.java | 45 ---- .../event/InformantRegisterRecordedV2.java | 45 ---- .../InformantRegisterCaseOrApplication.java | 91 -------- .../model/InformantRegisterDefendant.java | 115 --------- .../InformantRegisterDocumentRequest.java | 103 -------- .../model/InformantRegisterHearing.java | 64 ----- .../model/InformantRegisterHearingVenue.java | 64 ----- .../model/InformantRegisterOffence.java | 119 ---------- .../model/InformantRegisterRecipient.java | 79 ------- .../model/InformantRegisterResult.java | 62 ----- .../model/InformantRegisterResultData.java | 142 ----------- .../informant/model/ProsecutorResult.java | 79 ------- .../domain/informant/model/Verdict.java | 62 ----- ....event.informant-register-recorded-v2.json | 2 +- .../InformantRegisterGeneratedV2Test.java | 61 ----- .../InformantRegisterRecordedV2Test.java | 43 ---- ...nformantRegisterCaseOrApplicationTest.java | 44 ---- .../model/InformantRegisterDefendantTest.java | 63 ----- .../InformantRegisterDocumentRequestTest.java | 75 ------ .../model/InformantRegisterHearingTest.java | 41 ---- .../InformantRegisterHearingVenueTest.java | 50 ---- .../model/InformantRegisterOffenceTest.java | 66 ------ .../model/InformantRegisterRecipientTest.java | 44 ---- .../InformantRegisterResultDataTest.java | 47 ---- .../model/InformantRegisterResultTest.java | 51 ---- .../informant/model/ProsecutorResultTest.java | 37 --- .../domain/informant/model/VerdictTest.java | 56 ----- .../event/InformantRegisterEventListener.java | 10 +- .../InformantRegisterEventListenerTest.java | 12 +- .../InformantRegisterEventProcessor.java | 26 +-- .../InformantRegisterEventProcessorTest.java | 34 +-- .../src/yaml/subscriptions-descriptor.yaml | 2 + .../it/InformantRegisterEventFlowsIT.java | 220 ++++++++++++++++++ ...nformantRegisterDocumentRequestHelper.java | 14 ++ .../informantRegisterDocumentRequest.json | 6 +- .../informantRegisterResultData.json | 9 +- .../prosecutorResult.json | 8 +- .../view/ProsecutorResultsQueryView.java | 6 +- .../view/ProsecutorResultsQueryViewTest.java | 28 +-- 45 files changed, 335 insertions(+), 1852 deletions(-) delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplication.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendant.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequest.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearing.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenue.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffence.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipient.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResult.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultData.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResult.java delete mode 100644 results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/Verdict.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2Test.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2Test.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplicationTest.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendantTest.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequestTest.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingTest.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenueTest.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffenceTest.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipientTest.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultDataTest.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultTest.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResultTest.java delete mode 100644 results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictTest.java create mode 100644 results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java diff --git a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java index 4299a2c10..556155c87 100644 --- a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java +++ b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java @@ -14,10 +14,10 @@ import static uk.gov.moj.cpp.results.command.util.DefendantMapper.getDefendants; import uk.gov.justice.core.courts.ProsecutionCase; -import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; -import uk.gov.moj.cpp.results.domain.event.InformantRegisterRecordedV2; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; +import uk.gov.justice.results.courts.InformantRegisterGeneratedV2; +import uk.gov.justice.results.courts.InformantRegisterRecordedV2; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest; import uk.gov.justice.results.courts.GenerateInformantRegister; import uk.gov.justice.results.courts.NotifyInformantRegister; import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter; diff --git a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/DefendantMapper.java b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/DefendantMapper.java index 8becebf26..361730571 100644 --- a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/DefendantMapper.java +++ b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/DefendantMapper.java @@ -10,7 +10,7 @@ import uk.gov.justice.core.courts.Defendant; import uk.gov.justice.core.courts.PersonDefendant; import uk.gov.justice.core.courts.ProsecutionCase; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant; import java.time.format.DateTimeFormatter; import java.util.ArrayList; diff --git a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/ProsecutionCaseOrApplicationMapper.java b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/ProsecutionCaseOrApplicationMapper.java index b558df468..d782080a9 100644 --- a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/ProsecutionCaseOrApplicationMapper.java +++ b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/util/ProsecutionCaseOrApplicationMapper.java @@ -7,10 +7,10 @@ import uk.gov.justice.core.courts.Defendant; import uk.gov.justice.core.courts.ProsecutionCase; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterCaseOrApplication; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterOffence; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterResult; import java.util.ArrayList; import java.util.List; diff --git a/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java b/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java index 8705a243a..1f022c87c 100644 --- a/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java +++ b/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java @@ -15,8 +15,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient.informantRegisterRecipient; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterRecipient.informantRegisterRecipient; import static uk.gov.justice.services.core.annotation.Component.COMMAND_HANDLER; import static uk.gov.justice.services.messaging.Envelope.envelopeFrom; import static uk.gov.justice.services.messaging.Envelope.metadataFrom; @@ -31,18 +31,18 @@ import static uk.gov.justice.services.test.utils.core.reflection.ReflectionUtil.setField; import static uk.gov.moj.cpp.domains.InformantRegisterHelper.getInformantRegisterStreamId; -import uk.gov.moj.cpp.results.domain.event.InformantRegisterRecordedV2; -import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResultData; -import uk.gov.moj.cpp.results.domain.informant.model.Verdict; +import uk.gov.justice.results.courts.InformantRegisterRecordedV2; +import uk.gov.justice.results.courts.InformantRegisterGeneratedV2; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterCaseOrApplication; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterHearing; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterHearingVenue; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterOffence; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterRecipient; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterResult; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterResultData; +import uk.gov.justice.results.courts.informantRegisterDocument.Verdict; import uk.gov.justice.results.courts.GenerateInformantRegister; import uk.gov.justice.results.courts.InformantRegisterGenerated; import uk.gov.justice.results.courts.InformantRegisterNotificationIgnored; diff --git a/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java b/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java index cc907e888..315bd14b3 100644 --- a/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java +++ b/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java @@ -17,8 +17,8 @@ import uk.gov.justice.results.courts.InformantRegisterNotified; import uk.gov.justice.results.courts.InformantRegisterNotifiedV2; import uk.gov.justice.results.courts.NotifyInformantRegister; -import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient; +import uk.gov.justice.results.courts.InformantRegisterGeneratedV2; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterRecipient; import java.util.Collections; import java.util.List; @@ -50,7 +50,7 @@ public Object apply(final Object event) { } }), when(InformantRegisterGeneratedV2.class).apply(e -> { - final List informantRegisterWithRecipients = e.getInformantRegisterDocumentRequests().stream().filter( + final List informantRegisterWithRecipients = e.getInformantRegisterDocumentRequests().stream().filter( informantRegisterDocumentRequest -> nonNull(informantRegisterDocumentRequest.getRecipients()) && !informantRegisterDocumentRequest.getRecipients().isEmpty()) .collect(Collectors.toList()); if (isNotEmpty(informantRegisterWithRecipients)) { diff --git a/results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java b/results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java index faf20fbfb..c328733b1 100644 --- a/results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java +++ b/results-domain/results-domain-aggregate/src/test/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregateTest.java @@ -9,15 +9,15 @@ import static org.hamcrest.Matchers.hasSize; import static uk.gov.justice.results.courts.NotifyInformantRegister.notifyInformantRegister; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient.informantRegisterRecipient; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterRecipient.informantRegisterRecipient; import uk.gov.justice.results.courts.InformantRegisterNotificationIgnored; import uk.gov.justice.results.courts.InformantRegisterNotifiedV2; import uk.gov.justice.results.courts.NotifyInformantRegister; -import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient; +import uk.gov.justice.results.courts.InformantRegisterGeneratedV2; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterRecipient; import java.util.List; import java.util.UUID; diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2.java deleted file mode 100644 index 0d28c26ff..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2.java +++ /dev/null @@ -1,45 +0,0 @@ -package uk.gov.moj.cpp.results.domain.event; - -import uk.gov.justice.domain.annotation.Event; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -@Event("results.event.informant-register-generated-v2") -public class InformantRegisterGeneratedV2 { - - public static final String NAME = "results.event.informant-register-generated-v2"; - - private final List informantRegisterDocumentRequests; - private final Boolean systemGenerated; - - @JsonCreator - public InformantRegisterGeneratedV2( - @JsonProperty("informantRegisterDocumentRequests") final List informantRegisterDocumentRequests, - @JsonProperty("systemGenerated") final Boolean systemGenerated) { - this.informantRegisterDocumentRequests = informantRegisterDocumentRequests; - this.systemGenerated = systemGenerated; - } - - public List getInformantRegisterDocumentRequests() { return informantRegisterDocumentRequests; } - public Boolean getSystemGenerated() { return systemGenerated; } - - public static Builder informantRegisterGeneratedV2() { - return new Builder(); - } - - public static class Builder { - private List informantRegisterDocumentRequests; - private Boolean systemGenerated; - - public Builder withInformantRegisterDocumentRequests(final List informantRegisterDocumentRequests) { this.informantRegisterDocumentRequests = informantRegisterDocumentRequests; return this; } - public Builder withSystemGenerated(final Boolean systemGenerated) { this.systemGenerated = systemGenerated; return this; } - - public InformantRegisterGeneratedV2 build() { - return new InformantRegisterGeneratedV2(informantRegisterDocumentRequests, systemGenerated); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2.java deleted file mode 100644 index debe8bdb6..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2.java +++ /dev/null @@ -1,45 +0,0 @@ -package uk.gov.moj.cpp.results.domain.event; - -import uk.gov.justice.domain.annotation.Event; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; - -import java.util.UUID; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -@Event("results.event.informant-register-recorded-v2") -public class InformantRegisterRecordedV2 { - - public static final String NAME = "results.event.informant-register-recorded-v2"; - - private final UUID prosecutionAuthorityId; - private final InformantRegisterDocumentRequest informantRegister; - - @JsonCreator - public InformantRegisterRecordedV2( - @JsonProperty("prosecutionAuthorityId") final UUID prosecutionAuthorityId, - @JsonProperty("informantRegister") final InformantRegisterDocumentRequest informantRegister) { - this.prosecutionAuthorityId = prosecutionAuthorityId; - this.informantRegister = informantRegister; - } - - public UUID getProsecutionAuthorityId() { return prosecutionAuthorityId; } - public InformantRegisterDocumentRequest getInformantRegister() { return informantRegister; } - - public static Builder informantRegisterRecordedV2() { - return new Builder(); - } - - public static class Builder { - private UUID prosecutionAuthorityId; - private InformantRegisterDocumentRequest informantRegister; - - public Builder withProsecutionAuthorityId(final UUID prosecutionAuthorityId) { this.prosecutionAuthorityId = prosecutionAuthorityId; return this; } - public Builder withInformantRegister(final InformantRegisterDocumentRequest informantRegister) { this.informantRegister = informantRegister; return this; } - - public InformantRegisterRecordedV2 build() { - return new InformantRegisterRecordedV2(prosecutionAuthorityId, informantRegister); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplication.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplication.java deleted file mode 100644 index f9cc30a9c..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplication.java +++ /dev/null @@ -1,91 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class InformantRegisterCaseOrApplication { - - private final String caseOrApplicationReference; - private final String arrestSummonsNumber; - private final String applicationParticulars; - private final List offences; - private final List results; - - @JsonCreator - public InformantRegisterCaseOrApplication( - @JsonProperty("caseOrApplicationReference") final String caseOrApplicationReference, - @JsonProperty("arrestSummonsNumber") final String arrestSummonsNumber, - @JsonProperty("applicationParticulars") final String applicationParticulars, - @JsonProperty("offences") final List offences, - @JsonProperty("results") final List results) { - this.caseOrApplicationReference = caseOrApplicationReference; - this.arrestSummonsNumber = arrestSummonsNumber; - this.applicationParticulars = applicationParticulars; - this.offences = offences; - this.results = results; - } - - public String getCaseOrApplicationReference() { - return caseOrApplicationReference; - } - - public String getArrestSummonsNumber() { - return arrestSummonsNumber; - } - - public String getApplicationParticulars() { - return applicationParticulars; - } - - public List getOffences() { - return offences; - } - - public List getResults() { - return results; - } - - public static Builder informantRegisterCaseOrApplication() { - return new Builder(); - } - - public static class Builder { - private String caseOrApplicationReference; - private String arrestSummonsNumber; - private String applicationParticulars; - private List offences; - private List results; - - public Builder withCaseOrApplicationReference(final String caseOrApplicationReference) { - this.caseOrApplicationReference = caseOrApplicationReference; - return this; - } - - public Builder withArrestSummonsNumber(final String arrestSummonsNumber) { - this.arrestSummonsNumber = arrestSummonsNumber; - return this; - } - - public Builder withApplicationParticulars(final String applicationParticulars) { - this.applicationParticulars = applicationParticulars; - return this; - } - - public Builder withOffences(final List offences) { - this.offences = offences; - return this; - } - - public Builder withResults(final List results) { - this.results = results; - return this; - } - - public InformantRegisterCaseOrApplication build() { - return new InformantRegisterCaseOrApplication(caseOrApplicationReference, - arrestSummonsNumber, applicationParticulars, offences, results); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendant.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendant.java deleted file mode 100644 index d646a0246..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendant.java +++ /dev/null @@ -1,115 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class InformantRegisterDefendant { - - private final String name; - private final String address1; - private final String address2; - private final String address3; - private final String address4; - private final String address5; - private final String postCode; - private final String dateOfBirth; - private final String nationality; - private final String title; - private final String firstName; - private final String lastName; - private final List prosecutionCasesOrApplications; - private final List results; - - @JsonCreator - public InformantRegisterDefendant( - @JsonProperty("name") final String name, - @JsonProperty("address1") final String address1, - @JsonProperty("address2") final String address2, - @JsonProperty("address3") final String address3, - @JsonProperty("address4") final String address4, - @JsonProperty("address5") final String address5, - @JsonProperty("postCode") final String postCode, - @JsonProperty("dateOfBirth") final String dateOfBirth, - @JsonProperty("nationality") final String nationality, - @JsonProperty("title") final String title, - @JsonProperty("firstName") final String firstName, - @JsonProperty("lastName") final String lastName, - @JsonProperty("prosecutionCasesOrApplications") final List prosecutionCasesOrApplications, - @JsonProperty("results") final List results) { - this.name = name; - this.address1 = address1; - this.address2 = address2; - this.address3 = address3; - this.address4 = address4; - this.address5 = address5; - this.postCode = postCode; - this.dateOfBirth = dateOfBirth; - this.nationality = nationality; - this.title = title; - this.firstName = firstName; - this.lastName = lastName; - this.prosecutionCasesOrApplications = prosecutionCasesOrApplications; - this.results = results; - } - - public String getName() { return name; } - public String getAddress1() { return address1; } - public String getAddress2() { return address2; } - public String getAddress3() { return address3; } - public String getAddress4() { return address4; } - public String getAddress5() { return address5; } - public String getPostCode() { return postCode; } - public String getDateOfBirth() { return dateOfBirth; } - public String getNationality() { return nationality; } - public String getTitle() { return title; } - public String getFirstName() { return firstName; } - public String getLastName() { return lastName; } - public List getProsecutionCasesOrApplications() { return prosecutionCasesOrApplications; } - public List getResults() { return results; } - - public static Builder informantRegisterDefendant() { - return new Builder(); - } - - public static class Builder { - private String name; - private String address1; - private String address2; - private String address3; - private String address4; - private String address5; - private String postCode; - private String dateOfBirth; - private String nationality; - private String title; - private String firstName; - private String lastName; - private List prosecutionCasesOrApplications; - private List results; - - public Builder withName(final String name) { this.name = name; return this; } - public Builder withAddress1(final String address1) { this.address1 = address1; return this; } - public Builder withAddress2(final String address2) { this.address2 = address2; return this; } - public Builder withAddress3(final String address3) { this.address3 = address3; return this; } - public Builder withAddress4(final String address4) { this.address4 = address4; return this; } - public Builder withAddress5(final String address5) { this.address5 = address5; return this; } - public Builder withPostCode(final String postCode) { this.postCode = postCode; return this; } - public Builder withDateOfBirth(final String dateOfBirth) { this.dateOfBirth = dateOfBirth; return this; } - public Builder withNationality(final String nationality) { this.nationality = nationality; return this; } - public Builder withTitle(final String title) { this.title = title; return this; } - public Builder withFirstName(final String firstName) { this.firstName = firstName; return this; } - public Builder withLastName(final String lastName) { this.lastName = lastName; return this; } - public Builder withProsecutionCasesOrApplications(final List prosecutionCasesOrApplications) { - this.prosecutionCasesOrApplications = prosecutionCasesOrApplications; return this; - } - public Builder withResults(final List results) { this.results = results; return this; } - - public InformantRegisterDefendant build() { - return new InformantRegisterDefendant(name, address1, address2, address3, address4, address5, - postCode, dateOfBirth, nationality, title, firstName, lastName, - prosecutionCasesOrApplications, results); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequest.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequest.java deleted file mode 100644 index 47b673825..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequest.java +++ /dev/null @@ -1,103 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import java.time.ZonedDateTime; -import java.util.List; -import java.util.UUID; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class InformantRegisterDocumentRequest { - - private final ZonedDateTime registerDate; - private final ZonedDateTime hearingDate; - private final UUID hearingId; - private final UUID prosecutionAuthorityId; - private final String prosecutionAuthorityCode; - private final String prosecutionAuthorityOuCode; - private final String majorCreditorCode; - private final String prosecutionAuthorityName; - private final String fileName; - private final List recipients; - private final InformantRegisterHearingVenue hearingVenue; - private final UUID groupId; - - @JsonCreator - public InformantRegisterDocumentRequest( - @JsonProperty("registerDate") final ZonedDateTime registerDate, - @JsonProperty("hearingDate") final ZonedDateTime hearingDate, - @JsonProperty("hearingId") final UUID hearingId, - @JsonProperty("prosecutionAuthorityId") final UUID prosecutionAuthorityId, - @JsonProperty("prosecutionAuthorityCode") final String prosecutionAuthorityCode, - @JsonProperty("prosecutionAuthorityOuCode") final String prosecutionAuthorityOuCode, - @JsonProperty("majorCreditorCode") final String majorCreditorCode, - @JsonProperty("prosecutionAuthorityName") final String prosecutionAuthorityName, - @JsonProperty("fileName") final String fileName, - @JsonProperty("recipients") final List recipients, - @JsonProperty("hearingVenue") final InformantRegisterHearingVenue hearingVenue, - @JsonProperty("groupId") final UUID groupId) { - this.registerDate = registerDate; - this.hearingDate = hearingDate; - this.hearingId = hearingId; - this.prosecutionAuthorityId = prosecutionAuthorityId; - this.prosecutionAuthorityCode = prosecutionAuthorityCode; - this.prosecutionAuthorityOuCode = prosecutionAuthorityOuCode; - this.majorCreditorCode = majorCreditorCode; - this.prosecutionAuthorityName = prosecutionAuthorityName; - this.fileName = fileName; - this.recipients = recipients; - this.hearingVenue = hearingVenue; - this.groupId = groupId; - } - - public ZonedDateTime getRegisterDate() { return registerDate; } - public ZonedDateTime getHearingDate() { return hearingDate; } - public UUID getHearingId() { return hearingId; } - public UUID getProsecutionAuthorityId() { return prosecutionAuthorityId; } - public String getProsecutionAuthorityCode() { return prosecutionAuthorityCode; } - public String getProsecutionAuthorityOuCode() { return prosecutionAuthorityOuCode; } - public String getMajorCreditorCode() { return majorCreditorCode; } - public String getProsecutionAuthorityName() { return prosecutionAuthorityName; } - public String getFileName() { return fileName; } - public List getRecipients() { return recipients; } - public InformantRegisterHearingVenue getHearingVenue() { return hearingVenue; } - public UUID getGroupId() { return groupId; } - - public static Builder informantRegisterDocumentRequest() { - return new Builder(); - } - - public static class Builder { - private ZonedDateTime registerDate; - private ZonedDateTime hearingDate; - private UUID hearingId; - private UUID prosecutionAuthorityId; - private String prosecutionAuthorityCode; - private String prosecutionAuthorityOuCode; - private String majorCreditorCode; - private String prosecutionAuthorityName; - private String fileName; - private List recipients; - private InformantRegisterHearingVenue hearingVenue; - private UUID groupId; - - public Builder withRegisterDate(final ZonedDateTime registerDate) { this.registerDate = registerDate; return this; } - public Builder withHearingDate(final ZonedDateTime hearingDate) { this.hearingDate = hearingDate; return this; } - public Builder withHearingId(final UUID hearingId) { this.hearingId = hearingId; return this; } - public Builder withProsecutionAuthorityId(final UUID prosecutionAuthorityId) { this.prosecutionAuthorityId = prosecutionAuthorityId; return this; } - public Builder withProsecutionAuthorityCode(final String prosecutionAuthorityCode) { this.prosecutionAuthorityCode = prosecutionAuthorityCode; return this; } - public Builder withProsecutionAuthorityOuCode(final String prosecutionAuthorityOuCode) { this.prosecutionAuthorityOuCode = prosecutionAuthorityOuCode; return this; } - public Builder withMajorCreditorCode(final String majorCreditorCode) { this.majorCreditorCode = majorCreditorCode; return this; } - public Builder withProsecutionAuthorityName(final String prosecutionAuthorityName) { this.prosecutionAuthorityName = prosecutionAuthorityName; return this; } - public Builder withFileName(final String fileName) { this.fileName = fileName; return this; } - public Builder withRecipients(final List recipients) { this.recipients = recipients; return this; } - public Builder withHearingVenue(final InformantRegisterHearingVenue hearingVenue) { this.hearingVenue = hearingVenue; return this; } - public Builder withGroupId(final UUID groupId) { this.groupId = groupId; return this; } - - public InformantRegisterDocumentRequest build() { - return new InformantRegisterDocumentRequest(registerDate, hearingDate, hearingId, - prosecutionAuthorityId, prosecutionAuthorityCode, prosecutionAuthorityOuCode, - majorCreditorCode, prosecutionAuthorityName, fileName, recipients, hearingVenue, groupId); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearing.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearing.java deleted file mode 100644 index 0ead3ec24..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearing.java +++ /dev/null @@ -1,64 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class InformantRegisterHearing { - - private final String courtRoom; - private final String hearingStartTime; - private final List defendants; - - @JsonCreator - public InformantRegisterHearing( - @JsonProperty("courtRoom") final String courtRoom, - @JsonProperty("hearingStartTime") final String hearingStartTime, - @JsonProperty("defendants") final List defendants) { - this.courtRoom = courtRoom; - this.hearingStartTime = hearingStartTime; - this.defendants = defendants; - } - - public String getCourtRoom() { - return courtRoom; - } - - public String getHearingStartTime() { - return hearingStartTime; - } - - public List getDefendants() { - return defendants; - } - - public static Builder informantRegisterHearing() { - return new Builder(); - } - - public static class Builder { - private String courtRoom; - private String hearingStartTime; - private List defendants; - - public Builder withCourtRoom(final String courtRoom) { - this.courtRoom = courtRoom; - return this; - } - - public Builder withHearingStartTime(final String hearingStartTime) { - this.hearingStartTime = hearingStartTime; - return this; - } - - public Builder withDefendants(final List defendants) { - this.defendants = defendants; - return this; - } - - public InformantRegisterHearing build() { - return new InformantRegisterHearing(courtRoom, hearingStartTime, defendants); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenue.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenue.java deleted file mode 100644 index b0adf9327..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenue.java +++ /dev/null @@ -1,64 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class InformantRegisterHearingVenue { - - private final String ljaName; - private final String courtHouse; - private final List courtSessions; - - @JsonCreator - public InformantRegisterHearingVenue( - @JsonProperty("ljaName") final String ljaName, - @JsonProperty("courtHouse") final String courtHouse, - @JsonProperty("courtSessions") final List courtSessions) { - this.ljaName = ljaName; - this.courtHouse = courtHouse; - this.courtSessions = courtSessions; - } - - public String getLjaName() { - return ljaName; - } - - public String getCourtHouse() { - return courtHouse; - } - - public List getCourtSessions() { - return courtSessions; - } - - public static Builder informantRegisterHearingVenue() { - return new Builder(); - } - - public static class Builder { - private String ljaName; - private String courtHouse; - private List courtSessions; - - public Builder withLjaName(final String ljaName) { - this.ljaName = ljaName; - return this; - } - - public Builder withCourtHouse(final String courtHouse) { - this.courtHouse = courtHouse; - return this; - } - - public Builder withCourtSessions(final List courtSessions) { - this.courtSessions = courtSessions; - return this; - } - - public InformantRegisterHearingVenue build() { - return new InformantRegisterHearingVenue(ljaName, courtHouse, courtSessions); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffence.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffence.java deleted file mode 100644 index 845877249..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffence.java +++ /dev/null @@ -1,119 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class InformantRegisterOffence { - - private final String offenceCode; - private final String offenceTitle; - private final Integer orderIndex; - private final String originatingCaseUrn; - private final String pleaValue; - @JsonInclude(JsonInclude.Include.NON_NULL) - private final Verdict verdict; - private final List offenceResults; - - @JsonCreator - public InformantRegisterOffence( - @JsonProperty("offenceCode") final String offenceCode, - @JsonProperty("offenceTitle") final String offenceTitle, - @JsonProperty("orderIndex") final Integer orderIndex, - @JsonProperty("originatingCaseUrn") final String originatingCaseUrn, - @JsonProperty("pleaValue") final String pleaValue, - @JsonProperty("verdict") final Verdict verdict, - @JsonProperty("offenceResults") final List offenceResults) { - this.offenceCode = offenceCode; - this.offenceTitle = offenceTitle; - this.orderIndex = orderIndex; - this.originatingCaseUrn = originatingCaseUrn; - this.pleaValue = pleaValue; - this.verdict = verdict; - this.offenceResults = offenceResults; - } - - public String getOffenceCode() { - return offenceCode; - } - - public String getOffenceTitle() { - return offenceTitle; - } - - public Integer getOrderIndex() { - return orderIndex; - } - - public String getOriginatingCaseUrn() { - return originatingCaseUrn; - } - - public String getPleaValue() { - return pleaValue; - } - - public Verdict getVerdict() { - return verdict; - } - - public List getOffenceResults() { - return offenceResults; - } - - public static Builder informantRegisterOffence() { - return new Builder(); - } - - public static class Builder { - private String offenceCode; - private String offenceTitle; - private Integer orderIndex; - private String originatingCaseUrn; - private String pleaValue; - private Verdict verdict; - private List offenceResults; - - public Builder withOffenceCode(final String offenceCode) { - this.offenceCode = offenceCode; - return this; - } - - public Builder withOffenceTitle(final String offenceTitle) { - this.offenceTitle = offenceTitle; - return this; - } - - public Builder withOrderIndex(final Integer orderIndex) { - this.orderIndex = orderIndex; - return this; - } - - public Builder withOriginatingCaseUrn(final String originatingCaseUrn) { - this.originatingCaseUrn = originatingCaseUrn; - return this; - } - - public Builder withPleaValue(final String pleaValue) { - this.pleaValue = pleaValue; - return this; - } - - public Builder withVerdict(final Verdict verdict) { - this.verdict = verdict; - return this; - } - - public Builder withOffenceResults(final List offenceResults) { - this.offenceResults = offenceResults; - return this; - } - - public InformantRegisterOffence build() { - return new InformantRegisterOffence(offenceCode, offenceTitle, orderIndex, - originatingCaseUrn, pleaValue, verdict, offenceResults); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipient.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipient.java deleted file mode 100644 index fd31d7c40..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipient.java +++ /dev/null @@ -1,79 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.io.Serializable; - -public class InformantRegisterRecipient implements Serializable { - - private static final long serialVersionUID = 1L; - - private final String recipientName; - private final String emailAddress1; - private final String emailAddress2; - private final String emailTemplateName; - - @JsonCreator - public InformantRegisterRecipient( - @JsonProperty("recipientName") final String recipientName, - @JsonProperty("emailAddress1") final String emailAddress1, - @JsonProperty("emailAddress2") final String emailAddress2, - @JsonProperty("emailTemplateName") final String emailTemplateName) { - this.recipientName = recipientName; - this.emailAddress1 = emailAddress1; - this.emailAddress2 = emailAddress2; - this.emailTemplateName = emailTemplateName; - } - - public String getRecipientName() { - return recipientName; - } - - public String getEmailAddress1() { - return emailAddress1; - } - - public String getEmailAddress2() { - return emailAddress2; - } - - public String getEmailTemplateName() { - return emailTemplateName; - } - - public static Builder informantRegisterRecipient() { - return new Builder(); - } - - public static class Builder { - private String recipientName; - private String emailAddress1; - private String emailAddress2; - private String emailTemplateName; - - public Builder withRecipientName(final String recipientName) { - this.recipientName = recipientName; - return this; - } - - public Builder withEmailAddress1(final String emailAddress1) { - this.emailAddress1 = emailAddress1; - return this; - } - - public Builder withEmailAddress2(final String emailAddress2) { - this.emailAddress2 = emailAddress2; - return this; - } - - public Builder withEmailTemplateName(final String emailTemplateName) { - this.emailTemplateName = emailTemplateName; - return this; - } - - public InformantRegisterRecipient build() { - return new InformantRegisterRecipient(recipientName, emailAddress1, emailAddress2, emailTemplateName); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResult.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResult.java deleted file mode 100644 index dbb738e92..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResult.java +++ /dev/null @@ -1,62 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class InformantRegisterResult { - - private final String resultText; - private final String cjsResultCode; - private final InformantRegisterResultData resultData; - - @JsonCreator - public InformantRegisterResult( - @JsonProperty("resultText") final String resultText, - @JsonProperty("cjsResultCode") final String cjsResultCode, - @JsonProperty("resultData") final InformantRegisterResultData resultData) { - this.resultText = resultText; - this.cjsResultCode = cjsResultCode; - this.resultData = resultData; - } - - public String getResultText() { - return resultText; - } - - public String getCjsResultCode() { - return cjsResultCode; - } - - public InformantRegisterResultData getResultData() { - return resultData; - } - - public static Builder informantRegisterResult() { - return new Builder(); - } - - public static class Builder { - private String resultText; - private String cjsResultCode; - private InformantRegisterResultData resultData; - - public Builder withResultText(final String resultText) { - this.resultText = resultText; - return this; - } - - public Builder withCjsResultCode(final String cjsResultCode) { - this.cjsResultCode = cjsResultCode; - return this; - } - - public Builder withResultData(final InformantRegisterResultData resultData) { - this.resultData = resultData; - return this; - } - - public InformantRegisterResult build() { - return new InformantRegisterResult(resultText, cjsResultCode, resultData); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultData.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultData.java deleted file mode 100644 index d638a694b..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultData.java +++ /dev/null @@ -1,142 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class InformantRegisterResultData { - - private final String amount; - private final String nextHearingDate; - private final String nextCourtLocation; - private final String durationValue; - private final String durationUnit; - private final String durationStartDate; - private final String durationEndDate; - private final String secondaryDurationValue; - private final String secondaryDurationUnit; - - @JsonCreator - public InformantRegisterResultData( - @JsonProperty("amount") final String amount, - @JsonProperty("nextHearingDate") final String nextHearingDate, - @JsonProperty("nextCourtLocation") final String nextCourtLocation, - @JsonProperty("durationValue") final String durationValue, - @JsonProperty("durationUnit") final String durationUnit, - @JsonProperty("durationStartDate") final String durationStartDate, - @JsonProperty("durationEndDate") final String durationEndDate, - @JsonProperty("secondaryDurationValue") final String secondaryDurationValue, - @JsonProperty("secondaryDurationUnit") final String secondaryDurationUnit) { - this.amount = amount; - this.nextHearingDate = nextHearingDate; - this.nextCourtLocation = nextCourtLocation; - this.durationValue = durationValue; - this.durationUnit = durationUnit; - this.durationStartDate = durationStartDate; - this.durationEndDate = durationEndDate; - this.secondaryDurationValue = secondaryDurationValue; - this.secondaryDurationUnit = secondaryDurationUnit; - } - - public String getAmount() { - return amount; - } - - public String getNextHearingDate() { - return nextHearingDate; - } - - public String getNextCourtLocation() { - return nextCourtLocation; - } - - public String getDurationValue() { - return durationValue; - } - - public String getDurationUnit() { - return durationUnit; - } - - public String getDurationStartDate() { - return durationStartDate; - } - - public String getDurationEndDate() { - return durationEndDate; - } - - public String getSecondaryDurationValue() { - return secondaryDurationValue; - } - - public String getSecondaryDurationUnit() { - return secondaryDurationUnit; - } - - public static Builder informantRegisterResultData() { - return new Builder(); - } - - public static class Builder { - private String amount; - private String nextHearingDate; - private String nextCourtLocation; - private String durationValue; - private String durationUnit; - private String durationStartDate; - private String durationEndDate; - private String secondaryDurationValue; - private String secondaryDurationUnit; - - public Builder withAmount(final String amount) { - this.amount = amount; - return this; - } - - public Builder withNextHearingDate(final String nextHearingDate) { - this.nextHearingDate = nextHearingDate; - return this; - } - - public Builder withNextCourtLocation(final String nextCourtLocation) { - this.nextCourtLocation = nextCourtLocation; - return this; - } - - public Builder withDurationValue(final String durationValue) { - this.durationValue = durationValue; - return this; - } - - public Builder withDurationUnit(final String durationUnit) { - this.durationUnit = durationUnit; - return this; - } - - public Builder withDurationStartDate(final String durationStartDate) { - this.durationStartDate = durationStartDate; - return this; - } - - public Builder withDurationEndDate(final String durationEndDate) { - this.durationEndDate = durationEndDate; - return this; - } - - public Builder withSecondaryDurationValue(final String secondaryDurationValue) { - this.secondaryDurationValue = secondaryDurationValue; - return this; - } - - public Builder withSecondaryDurationUnit(final String secondaryDurationUnit) { - this.secondaryDurationUnit = secondaryDurationUnit; - return this; - } - - public InformantRegisterResultData build() { - return new InformantRegisterResultData(amount, nextHearingDate, nextCourtLocation, - durationValue, durationUnit, durationStartDate, durationEndDate, - secondaryDurationValue, secondaryDurationUnit); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResult.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResult.java deleted file mode 100644 index 56d1682db..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResult.java +++ /dev/null @@ -1,79 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import java.time.LocalDate; -import java.util.List; -import java.util.UUID; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class ProsecutorResult { - - private final LocalDate startDate; - private final LocalDate endDate; - private final UUID prosecutionAuthorityId; - private final String prosecutionAuthorityCode; - private final String prosecutionAuthorityName; - private final String prosecutionAuthorityOuCode; - private final String majorCreditorCode; - private final List hearingVenues; - - @JsonCreator - public ProsecutorResult( - @JsonProperty("startDate") final LocalDate startDate, - @JsonProperty("endDate") final LocalDate endDate, - @JsonProperty("prosecutionAuthorityId") final UUID prosecutionAuthorityId, - @JsonProperty("prosecutionAuthorityCode") final String prosecutionAuthorityCode, - @JsonProperty("prosecutionAuthorityName") final String prosecutionAuthorityName, - @JsonProperty("prosecutionAuthorityOuCode") final String prosecutionAuthorityOuCode, - @JsonProperty("majorCreditorCode") final String majorCreditorCode, - @JsonProperty("hearingVenues") final List hearingVenues) { - this.startDate = startDate; - this.endDate = endDate; - this.prosecutionAuthorityId = prosecutionAuthorityId; - this.prosecutionAuthorityCode = prosecutionAuthorityCode; - this.prosecutionAuthorityName = prosecutionAuthorityName; - this.prosecutionAuthorityOuCode = prosecutionAuthorityOuCode; - this.majorCreditorCode = majorCreditorCode; - this.hearingVenues = hearingVenues; - } - - public LocalDate getStartDate() { return startDate; } - public LocalDate getEndDate() { return endDate; } - public UUID getProsecutionAuthorityId() { return prosecutionAuthorityId; } - public String getProsecutionAuthorityCode() { return prosecutionAuthorityCode; } - public String getProsecutionAuthorityName() { return prosecutionAuthorityName; } - public String getProsecutionAuthorityOuCode() { return prosecutionAuthorityOuCode; } - public String getMajorCreditorCode() { return majorCreditorCode; } - public List getHearingVenues() { return hearingVenues; } - - public static Builder prosecutorResult() { - return new Builder(); - } - - public static class Builder { - private LocalDate startDate; - private LocalDate endDate; - private UUID prosecutionAuthorityId; - private String prosecutionAuthorityCode; - private String prosecutionAuthorityName; - private String prosecutionAuthorityOuCode; - private String majorCreditorCode; - private List hearingVenues; - - public Builder withStartDate(final LocalDate startDate) { this.startDate = startDate; return this; } - public Builder withEndDate(final LocalDate endDate) { this.endDate = endDate; return this; } - public Builder withProsecutionAuthorityId(final UUID prosecutionAuthorityId) { this.prosecutionAuthorityId = prosecutionAuthorityId; return this; } - public Builder withProsecutionAuthorityCode(final String prosecutionAuthorityCode) { this.prosecutionAuthorityCode = prosecutionAuthorityCode; return this; } - public Builder withProsecutionAuthorityName(final String prosecutionAuthorityName) { this.prosecutionAuthorityName = prosecutionAuthorityName; return this; } - public Builder withProsecutionAuthorityOuCode(final String prosecutionAuthorityOuCode) { this.prosecutionAuthorityOuCode = prosecutionAuthorityOuCode; return this; } - public Builder withMajorCreditorCode(final String majorCreditorCode) { this.majorCreditorCode = majorCreditorCode; return this; } - public Builder withHearingVenues(final List hearingVenues) { this.hearingVenues = hearingVenues; return this; } - - public ProsecutorResult build() { - return new ProsecutorResult(startDate, endDate, prosecutionAuthorityId, - prosecutionAuthorityCode, prosecutionAuthorityName, prosecutionAuthorityOuCode, - majorCreditorCode, hearingVenues); - } - } -} diff --git a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/Verdict.java b/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/Verdict.java deleted file mode 100644 index 48ce12113..000000000 --- a/results-domain/results-domain-common/src/main/java/uk/gov/moj/cpp/results/domain/informant/model/Verdict.java +++ /dev/null @@ -1,62 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class Verdict { - - private final String verdictCode; - private final String verdictDate; - private final String verdictType; - - @JsonCreator - public Verdict( - @JsonProperty("verdictCode") final String verdictCode, - @JsonProperty("verdictDate") final String verdictDate, - @JsonProperty("verdictType") final String verdictType) { - this.verdictCode = verdictCode; - this.verdictDate = verdictDate; - this.verdictType = verdictType; - } - - public String getVerdictCode() { - return verdictCode; - } - - public String getVerdictDate() { - return verdictDate; - } - - public String getVerdictType() { - return verdictType; - } - - public static Builder verdict() { - return new Builder(); - } - - public static class Builder { - private String verdictCode; - private String verdictDate; - private String verdictType; - - public Builder withVerdictCode(final String verdictCode) { - this.verdictCode = verdictCode; - return this; - } - - public Builder withVerdictDate(final String verdictDate) { - this.verdictDate = verdictDate; - return this; - } - - public Builder withVerdictType(final String verdictType) { - this.verdictType = verdictType; - return this; - } - - public Verdict build() { - return new Verdict(verdictCode, verdictDate, verdictType); - } - } -} diff --git a/results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json b/results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json index c63cb6ae4..8137cb78d 100644 --- a/results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json +++ b/results-domain/results-domain-common/src/main/resources/json/schema/results.event.informant-register-recorded-v2.json @@ -6,7 +6,7 @@ "properties": { "prosecutionAuthorityId": { "description": "The prosecuting authority reference data identifier", - "type": "string" + "$ref": "http://justice.gov.uk/domain/core/common/definitions.json#/definitions/uuid" }, "informantRegister": { "$ref": "http://justice.gov.uk/results/courts/informantRegisterDocument/informantRegisterDocumentRequest.json" diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2Test.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2Test.java deleted file mode 100644 index 4adf9a86f..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterGeneratedV2Test.java +++ /dev/null @@ -1,61 +0,0 @@ -package uk.gov.moj.cpp.results.domain.event; - -import static java.util.Collections.singletonList; -import static java.util.UUID.randomUUID; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; - -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue; - -import java.time.ZonedDateTime; -import java.util.List; - -import org.junit.jupiter.api.Test; - -public class InformantRegisterGeneratedV2Test { - - @Test - public void name_constantEqualsEventName() { - assertThat(InformantRegisterGeneratedV2.NAME, is("results.event.informant-register-generated-v2")); - } - - @Test - public void builder_setsDocumentRequestsList() { - final InformantRegisterDocumentRequest request = InformantRegisterDocumentRequest.informantRegisterDocumentRequest() - .withRegisterDate(ZonedDateTime.parse("2026-04-01T09:00:00Z")) - .withHearingId(randomUUID()) - .withProsecutionAuthorityId(randomUUID()) - .withProsecutionAuthorityCode("TFL") - .withHearingVenue(InformantRegisterHearingVenue.informantRegisterHearingVenue() - .withCourtHouse("Crown Court").build()) - .build(); - - final InformantRegisterGeneratedV2 event = InformantRegisterGeneratedV2.informantRegisterGeneratedV2() - .withInformantRegisterDocumentRequests(singletonList(request)) - .withSystemGenerated(true) - .build(); - - final List requests = event.getInformantRegisterDocumentRequests(); - assertThat(requests, hasSize(1)); - assertThat(event.getSystemGenerated(), is(true)); - } - - @Test - public void builder_systemGeneratedOptional() { - final InformantRegisterGeneratedV2 event = InformantRegisterGeneratedV2.informantRegisterGeneratedV2() - .withInformantRegisterDocumentRequests(singletonList( - InformantRegisterDocumentRequest.informantRegisterDocumentRequest() - .withRegisterDate(ZonedDateTime.parse("2026-04-01T09:00:00Z")) - .withHearingId(randomUUID()) - .withProsecutionAuthorityId(randomUUID()) - .withProsecutionAuthorityCode("TFL") - .withHearingVenue(InformantRegisterHearingVenue.informantRegisterHearingVenue() - .withCourtHouse("Crown Court").build()) - .build())) - .build(); - - assertThat(event.getSystemGenerated(), is((Boolean) null)); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2Test.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2Test.java deleted file mode 100644 index acaec639d..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/event/InformantRegisterRecordedV2Test.java +++ /dev/null @@ -1,43 +0,0 @@ -package uk.gov.moj.cpp.results.domain.event; - -import static java.util.UUID.randomUUID; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; - -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue; - -import java.time.ZonedDateTime; -import java.util.UUID; - -import org.junit.jupiter.api.Test; - -public class InformantRegisterRecordedV2Test { - - @Test - public void name_constantEqualsEventName() { - assertThat(InformantRegisterRecordedV2.NAME, is("results.event.informant-register-recorded-v2")); - } - - @Test - public void builder_setsProsecutionAuthorityId() { - final UUID prosecutionAuthorityId = randomUUID(); - final InformantRegisterDocumentRequest request = InformantRegisterDocumentRequest.informantRegisterDocumentRequest() - .withRegisterDate(ZonedDateTime.parse("2026-04-01T09:00:00Z")) - .withHearingId(randomUUID()) - .withProsecutionAuthorityId(prosecutionAuthorityId) - .withProsecutionAuthorityCode("TFL") - .withHearingVenue(InformantRegisterHearingVenue.informantRegisterHearingVenue() - .withCourtHouse("Crown Court").build()) - .build(); - - final InformantRegisterRecordedV2 event = InformantRegisterRecordedV2.informantRegisterRecordedV2() - .withProsecutionAuthorityId(prosecutionAuthorityId) - .withInformantRegister(request) - .build(); - - assertThat(event.getProsecutionAuthorityId(), is(prosecutionAuthorityId)); - assertThat(event.getInformantRegister(), is(notNullValue())); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplicationTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplicationTest.java deleted file mode 100644 index db4d01efd..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterCaseOrApplicationTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import static java.util.Collections.singletonList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.junit.jupiter.api.Test; - -public class InformantRegisterCaseOrApplicationTest { - - @Test - public void builder_setsAllFields() { - final InformantRegisterOffence offence = InformantRegisterOffence.informantRegisterOffence() - .withOffenceCode("AB001").withOffenceTitle("Theft").withOrderIndex(1).build(); - - final InformantRegisterCaseOrApplication coa = InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication() - .withCaseOrApplicationReference("URN-001") - .withArrestSummonsNumber("ASN-001") - .withApplicationParticulars("particulars") - .withOffences(singletonList(offence)) - .build(); - - assertThat(coa.getCaseOrApplicationReference(), is("URN-001")); - assertThat(coa.getArrestSummonsNumber(), is("ASN-001")); - assertThat(coa.getApplicationParticulars(), is("particulars")); - assertThat(coa.getOffences(), hasSize(1)); - assertThat(coa.getOffences().get(0).getOffenceCode(), is("AB001")); - } - - @Test - public void jacksonDeserialization_roundtrip() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - final String json = "{\"caseOrApplicationReference\":\"URN-001\",\"offences\":[" + - "{\"offenceCode\":\"AB001\",\"offenceTitle\":\"Theft\",\"orderIndex\":1}]}"; - - final InformantRegisterCaseOrApplication coa = mapper.readValue(json, InformantRegisterCaseOrApplication.class); - - assertThat(coa.getCaseOrApplicationReference(), is("URN-001")); - assertThat(coa.getOffences(), hasSize(1)); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendantTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendantTest.java deleted file mode 100644 index 3b67e05d7..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDefendantTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import static java.util.Collections.singletonList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.junit.jupiter.api.Test; - -public class InformantRegisterDefendantTest { - - @Test - public void builder_setsRequiredFields() { - final InformantRegisterDefendant defendant = InformantRegisterDefendant.informantRegisterDefendant() - .withName("John Smith") - .withAddress1("1 High Street") - .withFirstName("John") - .withLastName("Smith") - .build(); - - assertThat(defendant.getName(), is("John Smith")); - assertThat(defendant.getAddress1(), is("1 High Street")); - assertThat(defendant.getFirstName(), is("John")); - assertThat(defendant.getLastName(), is("Smith")); - } - - @Test - public void builder_optionalFieldsAreNull() { - final InformantRegisterDefendant defendant = InformantRegisterDefendant.informantRegisterDefendant() - .withName("John Smith").withAddress1("1 High Street").build(); - - assertThat(defendant.getAddress2(), is(nullValue())); - assertThat(defendant.getDateOfBirth(), is(nullValue())); - assertThat(defendant.getNationality(), is(nullValue())); - } - - @Test - public void builder_withCasesOrApplications_setsCorrectly() { - final InformantRegisterCaseOrApplication coa = InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication() - .withCaseOrApplicationReference("URN-001").build(); - - final InformantRegisterDefendant defendant = InformantRegisterDefendant.informantRegisterDefendant() - .withName("John").withAddress1("1 Street") - .withProsecutionCasesOrApplications(singletonList(coa)) - .build(); - - assertThat(defendant.getProsecutionCasesOrApplications(), hasSize(1)); - } - - @Test - public void jacksonDeserialization_roundtrip() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - final String json = "{\"name\":\"John Smith\",\"address1\":\"1 High Street\",\"firstName\":\"John\",\"lastName\":\"Smith\"}"; - - final InformantRegisterDefendant defendant = mapper.readValue(json, InformantRegisterDefendant.class); - - assertThat(defendant.getName(), is("John Smith")); - assertThat(defendant.getFirstName(), is("John")); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequestTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequestTest.java deleted file mode 100644 index 4838e2813..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterDocumentRequestTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import static java.util.Collections.singletonList; -import static java.util.UUID.randomUUID; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; - -import java.time.ZonedDateTime; -import java.util.UUID; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; - -import org.junit.jupiter.api.Test; - -public class InformantRegisterDocumentRequestTest { - - @Test - public void builder_setsAllRequiredFields() { - final UUID hearingId = randomUUID(); - final UUID prosecutionAuthorityId = randomUUID(); - final ZonedDateTime registerDate = ZonedDateTime.parse("2026-04-13T09:00:00Z"); - - final InformantRegisterHearingVenue venue = InformantRegisterHearingVenue.informantRegisterHearingVenue() - .withCourtHouse("Crown Court").build(); - - final InformantRegisterDocumentRequest request = InformantRegisterDocumentRequest.informantRegisterDocumentRequest() - .withRegisterDate(registerDate) - .withHearingDate(registerDate) - .withHearingId(hearingId) - .withProsecutionAuthorityId(prosecutionAuthorityId) - .withProsecutionAuthorityCode("TFL") - .withFileName("file.csv") - .withHearingVenue(venue) - .build(); - - assertThat(request.getRegisterDate(), is(registerDate)); - assertThat(request.getHearingId(), is(hearingId)); - assertThat(request.getProsecutionAuthorityId(), is(prosecutionAuthorityId)); - assertThat(request.getProsecutionAuthorityCode(), is("TFL")); - assertThat(request.getFileName(), is("file.csv")); - assertThat(request.getHearingVenue(), is(notNullValue())); - } - - @Test - public void jacksonDeserialization_nestedVerdictGraph_preservesVerdictCode() throws Exception { - final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()); - final String json = "{" + - "\"registerDate\":\"2026-04-13T09:00:00Z\"," + - "\"hearingDate\":\"2026-04-13T09:00:00Z\"," + - "\"hearingId\":\"" + randomUUID() + "\"," + - "\"prosecutionAuthorityId\":\"" + randomUUID() + "\"," + - "\"prosecutionAuthorityCode\":\"TFL\"," + - "\"fileName\":\"file.csv\"," + - "\"hearingVenue\":{\"courtHouse\":\"Crown Court\",\"courtSessions\":[{" + - "\"courtRoom\":\"Room 1\",\"hearingStartTime\":\"09:00\"," + - "\"defendants\":[{\"name\":\"John\",\"address1\":\"1 Street\"," + - "\"prosecutionCasesOrApplications\":[{" + - "\"caseOrApplicationReference\":\"URN-001\"," + - "\"offences\":[{\"offenceCode\":\"AB001\",\"offenceTitle\":\"Theft\",\"orderIndex\":1," + - "\"verdict\":{\"verdictCode\":\"G\",\"verdictDate\":\"2026-04-13\"}}]}]}]}]}}"; - - final InformantRegisterDocumentRequest request = mapper.readValue(json, InformantRegisterDocumentRequest.class); - - assertThat(request.getHearingVenue(), is(notNullValue())); - final String verdictCode = request.getHearingVenue() - .getCourtSessions().get(0) - .getDefendants().get(0) - .getProsecutionCasesOrApplications().get(0) - .getOffences().get(0) - .getVerdict().getVerdictCode(); - assertThat(verdictCode, is("G")); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingTest.java deleted file mode 100644 index a4af837ba..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import static java.util.Collections.singletonList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.junit.jupiter.api.Test; - -public class InformantRegisterHearingTest { - - @Test - public void builder_setsAllFields() { - final InformantRegisterDefendant defendant = InformantRegisterDefendant.informantRegisterDefendant() - .withName("John").withAddress1("1 Street").build(); - - final InformantRegisterHearing hearing = InformantRegisterHearing.informantRegisterHearing() - .withCourtRoom("Room 1") - .withHearingStartTime("09:00") - .withDefendants(singletonList(defendant)) - .build(); - - assertThat(hearing.getCourtRoom(), is("Room 1")); - assertThat(hearing.getHearingStartTime(), is("09:00")); - assertThat(hearing.getDefendants(), hasSize(1)); - } - - @Test - public void jacksonDeserialization_roundtrip() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - final String json = "{\"courtRoom\":\"Room 1\",\"hearingStartTime\":\"09:00\",\"defendants\":[" + - "{\"name\":\"John\",\"address1\":\"1 Street\"}]}"; - - final InformantRegisterHearing hearing = mapper.readValue(json, InformantRegisterHearing.class); - - assertThat(hearing.getCourtRoom(), is("Room 1")); - assertThat(hearing.getDefendants(), hasSize(1)); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenueTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenueTest.java deleted file mode 100644 index d71792cf9..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterHearingVenueTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import static java.util.Collections.singletonList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.junit.jupiter.api.Test; - -public class InformantRegisterHearingVenueTest { - - @Test - public void builder_setsRequiredFields() { - final InformantRegisterHearing hearing = InformantRegisterHearing.informantRegisterHearing() - .withCourtRoom("Room 1").withHearingStartTime("09:00").build(); - - final InformantRegisterHearingVenue venue = InformantRegisterHearingVenue.informantRegisterHearingVenue() - .withCourtHouse("Crown Court") - .withLjaName("LJA1") - .withCourtSessions(singletonList(hearing)) - .build(); - - assertThat(venue.getCourtHouse(), is("Crown Court")); - assertThat(venue.getLjaName(), is("LJA1")); - assertThat(venue.getCourtSessions(), hasSize(1)); - } - - @Test - public void builder_ljaNameOptional() { - final InformantRegisterHearingVenue venue = InformantRegisterHearingVenue.informantRegisterHearingVenue() - .withCourtHouse("Crown Court").build(); - - assertThat(venue.getLjaName(), is(nullValue())); - } - - @Test - public void jacksonDeserialization_roundtrip() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - final String json = "{\"courtHouse\":\"Crown Court\",\"courtSessions\":[" + - "{\"courtRoom\":\"Room 1\",\"hearingStartTime\":\"09:00\",\"defendants\":[]}]}"; - - final InformantRegisterHearingVenue venue = mapper.readValue(json, InformantRegisterHearingVenue.class); - - assertThat(venue.getCourtHouse(), is("Crown Court")); - assertThat(venue.getCourtSessions(), hasSize(1)); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffenceTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffenceTest.java deleted file mode 100644 index 745aa3706..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterOffenceTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.junit.jupiter.api.Test; - -public class InformantRegisterOffenceTest { - - @Test - public void builder_setsVerdictField() { - final Verdict verdict = Verdict.verdict().withVerdictCode("G").withVerdictDate("2026-04-13").build(); - - final InformantRegisterOffence offence = InformantRegisterOffence.informantRegisterOffence() - .withOffenceCode("AB001") - .withOffenceTitle("Theft") - .withOrderIndex(1) - .withVerdict(verdict) - .build(); - - assertThat(offence.getVerdict(), is(notNullValue())); - assertThat(offence.getVerdict().getVerdictCode(), is("G")); - assertThat(offence.getVerdict().getVerdictDate(), is("2026-04-13")); - assertThat(offence.getOffenceCode(), is("AB001")); - assertThat(offence.getOffenceTitle(), is("Theft")); - assertThat(offence.getOrderIndex(), is(1)); - } - - @Test - public void builder_withoutVerdict_verdictIsNull() { - final InformantRegisterOffence offence = InformantRegisterOffence.informantRegisterOffence() - .withOffenceCode("AB001") - .withOffenceTitle("Theft") - .withOrderIndex(1) - .build(); - - assertThat(offence.getVerdict(), is(nullValue())); - } - - @Test - public void jacksonDeserialization_withVerdictObject_deserializesCorrectly() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - final String json = "{\"offenceCode\":\"AB001\",\"offenceTitle\":\"Theft\",\"orderIndex\":1," + - "\"verdict\":{\"verdictCode\":\"G\",\"verdictDate\":\"2026-04-13\",\"verdictType\":\"FOUND_GUILTY\"}}"; - - final InformantRegisterOffence offence = mapper.readValue(json, InformantRegisterOffence.class); - - assertThat(offence.getOffenceCode(), is("AB001")); - assertThat(offence.getVerdict(), is(notNullValue())); - assertThat(offence.getVerdict().getVerdictCode(), is("G")); - } - - @Test - public void jacksonDeserialization_withNoVerdict_verdictIsNull() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - final String json = "{\"offenceCode\":\"AB001\",\"offenceTitle\":\"Theft\",\"orderIndex\":1}"; - - final InformantRegisterOffence offence = mapper.readValue(json, InformantRegisterOffence.class); - - assertThat(offence.getVerdict(), is(nullValue())); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipientTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipientTest.java deleted file mode 100644 index 14f3783e8..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterRecipientTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.junit.jupiter.api.Test; - -public class InformantRegisterRecipientTest { - - @Test - public void builder_setsRequiredFields() { - final InformantRegisterRecipient recipient = InformantRegisterRecipient.informantRegisterRecipient() - .withRecipientName("Prosecutor A") - .withEmailAddress1("a@example.com") - .withEmailTemplateName("TEMPLATE_1") - .build(); - - assertThat(recipient.getRecipientName(), is("Prosecutor A")); - assertThat(recipient.getEmailAddress1(), is("a@example.com")); - assertThat(recipient.getEmailTemplateName(), is("TEMPLATE_1")); - } - - @Test - public void builder_emailAddress2IsOptional() { - final InformantRegisterRecipient recipient = InformantRegisterRecipient.informantRegisterRecipient() - .withRecipientName("Prosecutor A").withEmailAddress1("a@example.com").build(); - - assertThat(recipient.getEmailAddress2(), is(nullValue())); - } - - @Test - public void jacksonDeserialization_roundtrip() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - final String json = "{\"recipientName\":\"Prosecutor A\",\"emailAddress1\":\"a@example.com\",\"emailTemplateName\":\"T1\"}"; - - final InformantRegisterRecipient recipient = mapper.readValue(json, InformantRegisterRecipient.class); - - assertThat(recipient.getRecipientName(), is("Prosecutor A")); - assertThat(recipient.getEmailAddress1(), is("a@example.com")); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultDataTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultDataTest.java deleted file mode 100644 index 9b59eae16..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultDataTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.junit.jupiter.api.Test; - -public class InformantRegisterResultDataTest { - - @Test - public void builder_setsFields() { - final InformantRegisterResultData data = InformantRegisterResultData.informantRegisterResultData() - .withAmount("50.00") - .withDurationValue("6") - .withDurationUnit("MONTHS") - .withNextHearingDate("2026-09-01T10:00:00Z") - .build(); - - assertThat(data.getAmount(), is("50.00")); - assertThat(data.getDurationValue(), is("6")); - assertThat(data.getDurationUnit(), is("MONTHS")); - assertThat(data.getNextHearingDate(), is("2026-09-01T10:00:00Z")); - } - - @Test - public void builder_allFieldsOptional() { - final InformantRegisterResultData data = InformantRegisterResultData.informantRegisterResultData().build(); - - assertThat(data.getAmount(), is(nullValue())); - assertThat(data.getDurationValue(), is(nullValue())); - } - - @Test - public void jacksonDeserialization_roundtrip() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - final String json = "{\"amount\":\"50.00\",\"durationValue\":\"6\",\"durationUnit\":\"MONTHS\"}"; - - final InformantRegisterResultData data = mapper.readValue(json, InformantRegisterResultData.class); - - assertThat(data.getAmount(), is("50.00")); - assertThat(data.getDurationValue(), is("6")); - assertThat(data.getDurationUnit(), is("MONTHS")); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultTest.java deleted file mode 100644 index 4f14ebb56..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/InformantRegisterResultTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.junit.jupiter.api.Test; - -public class InformantRegisterResultTest { - - @Test - public void builder_setsAllFields() { - final InformantRegisterResultData data = InformantRegisterResultData.informantRegisterResultData() - .withAmount("100.00").build(); - - final InformantRegisterResult result = InformantRegisterResult.informantRegisterResult() - .withResultText("Fined") - .withCjsResultCode("F") - .withResultData(data) - .build(); - - assertThat(result.getResultText(), is("Fined")); - assertThat(result.getCjsResultCode(), is("F")); - assertThat(result.getResultData(), is(notNullValue())); - assertThat(result.getResultData().getAmount(), is("100.00")); - } - - @Test - public void builder_optionalFieldsAreNull() { - final InformantRegisterResult result = InformantRegisterResult.informantRegisterResult() - .withResultText("Fined") - .build(); - - assertThat(result.getCjsResultCode(), is(nullValue())); - assertThat(result.getResultData(), is(nullValue())); - } - - @Test - public void jacksonDeserialization_roundtrip() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - final String json = "{\"resultText\":\"Fined\",\"cjsResultCode\":\"F\"}"; - - final InformantRegisterResult result = mapper.readValue(json, InformantRegisterResult.class); - - assertThat(result.getResultText(), is("Fined")); - assertThat(result.getCjsResultCode(), is("F")); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResultTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResultTest.java deleted file mode 100644 index a299e377c..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/ProsecutorResultTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import static java.time.LocalDate.parse; -import static java.util.UUID.randomUUID; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; - -import org.junit.jupiter.api.Test; - -public class ProsecutorResultTest { - - @Test - public void builder_setsRequiredFields() { - final ProsecutorResult result = ProsecutorResult.prosecutorResult() - .withStartDate(parse("2026-04-01")) - .withProsecutionAuthorityId(randomUUID()) - .withProsecutionAuthorityCode("TFL") - .build(); - - assertThat(result.getStartDate(), is(parse("2026-04-01"))); - assertThat(result.getProsecutionAuthorityCode(), is("TFL")); - } - - @Test - public void builder_optionalFieldsAreNull() { - final ProsecutorResult result = ProsecutorResult.prosecutorResult() - .withStartDate(parse("2026-04-01")) - .withProsecutionAuthorityId(randomUUID()) - .withProsecutionAuthorityCode("TFL") - .build(); - - assertThat(result.getEndDate(), is(nullValue())); - assertThat(result.getProsecutionAuthorityName(), is(nullValue())); - assertThat(result.getHearingVenues(), is(nullValue())); - } -} diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictTest.java deleted file mode 100644 index 8bd3d5a66..000000000 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package uk.gov.moj.cpp.results.domain.informant.model; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.junit.jupiter.api.Test; - -public class VerdictTest { - - @Test - public void builder_setsAllThreeFields() { - final Verdict verdict = Verdict.verdict() - .withVerdictCode("G") - .withVerdictDate("2026-04-13") - .withVerdictType("FOUND_GUILTY") - .build(); - - assertThat(verdict.getVerdictCode(), is("G")); - assertThat(verdict.getVerdictDate(), is("2026-04-13")); - assertThat(verdict.getVerdictType(), is("FOUND_GUILTY")); - } - - @Test - public void builder_toleratesNullFields() { - final Verdict verdict = Verdict.verdict().build(); - - assertThat(verdict.getVerdictCode(), is(nullValue())); - assertThat(verdict.getVerdictDate(), is(nullValue())); - assertThat(verdict.getVerdictType(), is(nullValue())); - } - - @Test - public void jacksonDeserialization_roundtrip_preservesAllFields() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - final String json = "{\"verdictCode\":\"G\",\"verdictDate\":\"2026-04-13\",\"verdictType\":\"FOUND_GUILTY\"}"; - - final Verdict verdict = mapper.readValue(json, Verdict.class); - - assertThat(verdict.getVerdictCode(), is("G")); - assertThat(verdict.getVerdictDate(), is("2026-04-13")); - assertThat(verdict.getVerdictType(), is("FOUND_GUILTY")); - } - - @Test - public void jacksonDeserialization_missingFields_producesNulls() throws Exception { - final ObjectMapper mapper = new ObjectMapper(); - final Verdict verdict = mapper.readValue("{}", Verdict.class); - - assertThat(verdict.getVerdictCode(), is(nullValue())); - assertThat(verdict.getVerdictDate(), is(nullValue())); - assertThat(verdict.getVerdictType(), is(nullValue())); - } -} diff --git a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java index 81d1edef3..5b2584e21 100644 --- a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java +++ b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java @@ -11,8 +11,8 @@ import uk.gov.justice.results.courts.InformantRegisterGenerated; import uk.gov.justice.results.courts.InformantRegisterNotified; import uk.gov.justice.results.courts.InformantRegisterNotifiedV2; -import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; -import uk.gov.moj.cpp.results.domain.event.InformantRegisterRecordedV2; +import uk.gov.justice.results.courts.InformantRegisterGeneratedV2; +import uk.gov.justice.results.courts.InformantRegisterRecordedV2; import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter; import uk.gov.justice.services.core.annotation.Handles; import uk.gov.justice.services.core.annotation.ServiceComponent; @@ -67,8 +67,8 @@ public void saveInformantRegister(final JsonEnvelope event) { @Handles("results.event.informant-register-recorded-v2") public void saveInformantRegisterV2(final JsonEnvelope event) { final JsonObject informantRegisterDocumentRequestJson = event.payloadAsJsonObject().getJsonObject(INFORMANT_REGISTER_REQUEST_PARAM); - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest documentRequest = - jsonObjectToObjectConverter.convert(informantRegisterDocumentRequestJson, uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.class); + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest documentRequest = + jsonObjectToObjectConverter.convert(informantRegisterDocumentRequestJson, uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest.class); final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); informantRegisterEntity.setId(randomUUID()); @@ -129,7 +129,7 @@ public void generateInformantRegisterV2(final JsonEnvelope event) { }); informantRegisterGenerated.getInformantRegisterDocumentRequests().stream() - .map(uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest::getHearingId) + .map(uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest::getHearingId) .forEach(hearingId -> { final List informantRegistersList = informantRegisterRepository.findByHearingIdAndStatusRecorded(hearingId); informantRegistersList.forEach(informantRegisterEntity -> informantRegisterEntity.setProcessedOn(currentDateTime)); diff --git a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java index 05ca6e743..68abc86dd 100644 --- a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java +++ b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java @@ -31,8 +31,8 @@ import uk.gov.justice.services.common.converter.ObjectToJsonObjectConverter; import uk.gov.justice.services.common.converter.jackson.ObjectMapperProducer; import uk.gov.justice.services.messaging.JsonEnvelope; -import uk.gov.moj.cpp.results.domain.event.InformantRegisterGeneratedV2; -import uk.gov.moj.cpp.results.domain.event.InformantRegisterRecordedV2; +import uk.gov.justice.results.courts.InformantRegisterGeneratedV2; +import uk.gov.justice.results.courts.InformantRegisterRecordedV2; import uk.gov.moj.cpp.results.persist.InformantRegisterRepository; import uk.gov.moj.cpp.results.persist.entity.InformantRegisterEntity; @@ -111,8 +111,8 @@ public void saveInformantRegisterV2_shouldSaveEntityFromLocalDocumentRequest() { final String ouCode = randomAlphanumeric(10); final ZonedDateTime registerDate = ZonedDateTime.parse("2026-04-13T09:00:00Z"); - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest localDocumentRequest = - uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest() + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest localDocumentRequest = + uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest() .withProsecutionAuthorityId(prosecutionAuthId) .withProsecutionAuthorityOuCode(ouCode) .withRegisterDate(registerDate) @@ -174,8 +174,8 @@ public void generateInformantRegisterV2_shouldSetEntityStatusToGenerated() { final UUID prosecutionAuthId = randomUUID(); final ZonedDateTime registerDate = ZonedDateTime.parse("2026-04-13T09:00:00Z"); - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest localDocumentRequest = - uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest() + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest localDocumentRequest = + uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest() .withProsecutionAuthorityId(prosecutionAuthId) .withRegisterDate(registerDate) .build(); diff --git a/results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java b/results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java index 37b1cc459..45460faa3 100644 --- a/results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java +++ b/results-event/results-event-processor/src/main/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessor.java @@ -291,8 +291,8 @@ private byte[] generateCsvDocumentV2(final List informantRegistersBy } private void mapToInformantRegisterDocumentsV2(final List informantRegisters, final JsonObject informantRegisterJson) { - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest documentRequest = - jsonObjectToObjectConverter.convert(informantRegisterJson, uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.class); + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest documentRequest = + jsonObjectToObjectConverter.convert(informantRegisterJson, uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest.class); documentRequest.getHearingVenue().getCourtSessions().forEach(courtSession -> courtSession.getDefendants().forEach(defendant -> { if (isNotEmpty(defendant.getResults())) { defendant.getResults().forEach(result -> buildInformantRegisterV2(informantRegisters, documentRequest, courtSession, defendant, null, result, null, null)); @@ -315,13 +315,13 @@ private void mapToInformantRegisterDocumentsV2(final List informantRegisters, - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest documentRequest, - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing courtSession, - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant defendant, - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication caseOrApplication, - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult result, - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence offence, - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult offenceResult) { + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest documentRequest, + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterHearing courtSession, + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant defendant, + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterCaseOrApplication caseOrApplication, + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterResult result, + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterOffence offence, + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterResult offenceResult) { final InformantRegisterDocument.Builder informantRegisterDocumentBuilder = informantRegisterDocument() .withInfDestID(EMPTY_STRING) @@ -359,8 +359,8 @@ private void buildInformantRegisterV2( private void buildOffenceDetailsV2( final InformantRegisterDocument.Builder builder, - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence offence, - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult offenceResult) { + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterOffence offence, + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterResult offenceResult) { builder.withOffenceCode(offence.getOffenceCode()); builder.withOffenceTitle(offence.getOffenceTitle()); builder.withPleaValue(offence.getPleaValue()); @@ -372,12 +372,12 @@ private void buildOffenceDetailsV2( private void buildResultV2( final InformantRegisterDocument.Builder builder, - final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterResult offenceResult) { + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterResult offenceResult) { builder.withCjsResultCode(offenceResult.getCjsResultCode()); builder.withResultText(offenceResult.getResultText().trim()); } - private String getAddressV2(final uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant defendant) { + private String getAddressV2(final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant defendant) { final List addressAsList = Arrays.asList(defendant.getAddress1(), defendant.getAddress2(), defendant.getAddress3(), defendant.getAddress4(), defendant.getAddress5()); return addressAsList .stream() diff --git a/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java b/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java index 6022e9ead..051e91168 100644 --- a/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java +++ b/results-event/results-event-processor/src/test/java/uk/gov/moj/cpp/results/event/processor/InformantRegisterEventProcessorTest.java @@ -21,14 +21,14 @@ import static uk.gov.justice.services.test.utils.core.enveloper.EnveloperFactory.createEnveloper; import static uk.gov.justice.services.test.utils.core.messaging.MetadataBuilderFactory.metadataWithRandomUUID; import static uk.gov.justice.services.test.utils.core.reflection.ReflectionUtil.setField; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant.informantRegisterDefendant; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing.informantRegisterHearing; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue.informantRegisterHearingVenue; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence.informantRegisterOffence; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient.informantRegisterRecipient; -import static uk.gov.moj.cpp.results.domain.informant.model.Verdict.verdict; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant.informantRegisterDefendant; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterHearing.informantRegisterHearing; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterHearingVenue.informantRegisterHearingVenue; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterOffence.informantRegisterOffence; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterRecipient.informantRegisterRecipient; +import static uk.gov.justice.results.courts.informantRegisterDocument.Verdict.verdict; import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter; import uk.gov.justice.services.common.converter.ObjectToJsonObjectConverter; @@ -40,15 +40,15 @@ 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.results.domain.event.InformantRegisterGeneratedV2; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterRecipient; -import uk.gov.moj.cpp.results.domain.informant.model.Verdict; +import uk.gov.justice.results.courts.InformantRegisterGeneratedV2; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterCaseOrApplication; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterHearing; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterHearingVenue; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterOffence; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterRecipient; +import uk.gov.justice.results.courts.informantRegisterDocument.Verdict; import uk.gov.moj.cpp.results.event.service.ApplicationParameters; import uk.gov.moj.cpp.results.event.service.NotificationNotifyService; diff --git a/results-event/results-event-processor/src/yaml/subscriptions-descriptor.yaml b/results-event/results-event-processor/src/yaml/subscriptions-descriptor.yaml index 850ddccc7..5409079ea 100644 --- a/results-event/results-event-processor/src/yaml/subscriptions-descriptor.yaml +++ b/results-event/results-event-processor/src/yaml/subscriptions-descriptor.yaml @@ -31,6 +31,8 @@ subscriptions_descriptor: schema_uri: http://justice.gov.uk/results/courts/informant-register-generated-v2.json - name: results.event.informant-register-notified schema_uri: http://justice.gov.uk/results/courts/informant-register-notified.json + - name: results.event.informant-register-notified-v2 + schema_uri: http://justice.gov.uk/results/courts/informant-register-notified-v2.json - name: results.event.appeal-update-notification-requested schema_uri: http://moj.gov.uk/cpp/results/domain/event/results.event.appeal-update-notification-requested.json - name: results.event.publish-to-dcs diff --git a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java new file mode 100644 index 000000000..d767ffb1d --- /dev/null +++ b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java @@ -0,0 +1,220 @@ +package uk.gov.moj.cpp.results.it; + +import static java.time.ZoneOffset.UTC; +import static java.time.ZonedDateTime.now; +import static java.util.UUID.randomUUID; +import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric; +import static uk.gov.justice.services.messaging.JsonEnvelope.metadataBuilder; +import static uk.gov.justice.services.messaging.JsonObjects.createArrayBuilder; +import static uk.gov.justice.services.messaging.JsonObjects.createObjectBuilder; +import static uk.gov.justice.services.messaging.JsonObjects.createReader; +import static uk.gov.moj.cpp.results.it.utils.FileUtil.getPayload; +import static uk.gov.moj.cpp.results.it.utils.QueueUtil.privateEvents; +import static uk.gov.moj.cpp.results.it.utils.QueueUtil.sendMessage; +import static uk.gov.moj.cpp.results.it.utils.WireMockStubUtils.setupUsersGroupQueryStub; + +import uk.gov.justice.services.messaging.Metadata; +import uk.gov.moj.cpp.results.it.helper.InformantRegisterDocumentRequestHelper; + +import java.io.StringReader; +import java.time.ZonedDateTime; +import java.util.Random; +import java.util.UUID; + +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.json.JsonObject; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Exercises the four informant-register domain-event flows at the event-listener boundary by publishing each event + * directly onto the internal {@code results.event} topic and asserting the resulting view-store projection: + * + *
    + *
  • {@code results.event.informant-register-recorded} (V1, core namespace) -> RECORDED projection
  • + *
  • {@code results.event.informant-register-recorded-v2} (V2, results namespace) -> RECORDED projection
  • + *
  • {@code results.event.informant-register-generated} (V1) advances a RECORDED register past RECORDED
  • + *
  • {@code results.event.informant-register-generated-v2} (V2) advances a RECORDED register past RECORDED
  • + *
+ * + *

The current command path only ever emits the V2 events, so the V1 events are produced here synthetically to + * confirm their listener handlers still resolve and project correctly (this is the listener-resolution guarantee the + * V2/generated-class consolidation depends on). The {@code generated} events only transition existing RECORDED + * registers, so each generate test first injects the matching {@code recorded} event.

+ */ +public class InformantRegisterEventFlowsIT { + + private static final String EVENT_RECORDED = "results.event.informant-register-recorded"; + private static final String EVENT_RECORDED_V2 = "results.event.informant-register-recorded-v2"; + private static final String EVENT_GENERATED = "results.event.informant-register-generated"; + private static final String EVENT_GENERATED_V2 = "results.event.informant-register-generated-v2"; + + private static final String V2_DOCUMENT_REQUEST_RESOURCE = + "json/informant-register/results.add-informant-register-document-request.json"; + + private static final Random RANDOM = new Random(); + + private InformantRegisterDocumentRequestHelper helper; + private MessageProducer privateProducer; + + @BeforeAll + public static void setupStubs() { + setupUsersGroupQueryStub(); + } + + @BeforeEach + public void setup() { + helper = new InformantRegisterDocumentRequestHelper(); + privateProducer = privateEvents.createProducer(); + } + + @AfterEach + public void tearDown() throws JMSException { + privateProducer.close(); + } + + @Test + public void recordedV2EventShouldProjectRecordedRegister() { + final UUID prosecutionAuthorityId = randomUUID(); + final ZonedDateTime registerDate = now(UTC); + final JsonObject documentRequest = v2DocumentRequest(prosecutionAuthorityId, registerDate, randomUUID()); + + publishRecorded(EVENT_RECORDED_V2, prosecutionAuthorityId, documentRequest); + + helper.verifyInformantRegisterRequestsExists(prosecutionAuthorityId); + } + + @Test + public void generatedV2EventShouldAdvanceRecordedRegister() { + final UUID prosecutionAuthorityId = randomUUID(); + final ZonedDateTime registerDate = now(UTC); + final JsonObject documentRequest = v2DocumentRequest(prosecutionAuthorityId, registerDate, randomUUID()); + + publishRecorded(EVENT_RECORDED_V2, prosecutionAuthorityId, documentRequest); + helper.verifyInformantRegisterRequestsExists(prosecutionAuthorityId); + + publishGenerated(EVENT_GENERATED_V2, documentRequest); + helper.verifyInformantRegisterNoLongerRecorded(prosecutionAuthorityId); + } + + @Test + public void recordedEventShouldProjectRecordedRegister() { + final UUID prosecutionAuthorityId = randomUUID(); + final ZonedDateTime registerDate = now(UTC); + final JsonObject documentRequest = v1DocumentRequest(prosecutionAuthorityId, registerDate, randomUUID()); + + publishRecorded(EVENT_RECORDED, prosecutionAuthorityId, documentRequest); + + helper.verifyInformantRegisterRequestsExists(prosecutionAuthorityId); + } + + @Test + public void generatedEventShouldAdvanceRecordedRegister() { + final UUID prosecutionAuthorityId = randomUUID(); + final ZonedDateTime registerDate = now(UTC); + final JsonObject documentRequest = v1DocumentRequest(prosecutionAuthorityId, registerDate, randomUUID()); + + publishRecorded(EVENT_RECORDED, prosecutionAuthorityId, documentRequest); + helper.verifyInformantRegisterRequestsExists(prosecutionAuthorityId); + + publishGenerated(EVENT_GENERATED, documentRequest); + helper.verifyInformantRegisterNoLongerRecorded(prosecutionAuthorityId); + } + + private void publishRecorded(final String eventName, final UUID prosecutionAuthorityId, final JsonObject documentRequest) { + final JsonObject payload = createObjectBuilder() + .add("prosecutionAuthorityId", prosecutionAuthorityId.toString()) + .add("informantRegister", documentRequest) + .build(); + sendMessage(privateProducer, eventName, payload, metadataFor(eventName)); + } + + private void publishGenerated(final String eventName, final JsonObject documentRequest) { + // Both generated schemas are additionalProperties:false and expose only + // informantRegisterDocumentRequests + systemGenerated (the V1 schema also allows materialId), + // so no extra fields may be added or the envelope is rejected at validation. + final JsonObject payload = createObjectBuilder() + .add("informantRegisterDocumentRequests", createArrayBuilder().add(documentRequest)) + .add("systemGenerated", true) + .build(); + sendMessage(privateProducer, eventName, payload, metadataFor(eventName)); + } + + private Metadata metadataFor(final String eventName) { + // Inject under a dedicated test source (not "results") with a high random event number so the + // event buffer does not treat these synthetic events as already-seen for the real results source. + // A fresh random stream id keeps each injected event at position 1 of its own stream. + return metadataBuilder() + .withId(randomUUID()) + .withStreamId(randomUUID()) + .withPosition(1) + .withPreviousEventNumber(123) + .withEventNumber(Math.abs(RANDOM.nextLong())) + .withSource("event-indexer-test") + .withName(eventName) + .withUserId(randomUUID().toString()) + .build(); + } + + /** + * A complete, schema-valid V2 (results-namespace) informant-register document request, built by reusing the + * command-side test fixture so it stays in lock-step with the production schema. + */ + private JsonObject v2DocumentRequest(final UUID prosecutionAuthorityId, final ZonedDateTime registerDate, final UUID hearingId) { + final String body = getPayload(V2_DOCUMENT_REQUEST_RESOURCE) + .replaceAll("%PROSECUTION_AUTHORITY_ID%", prosecutionAuthorityId.toString()) + .replaceAll("%PROSECUTION_AUTHORITY_CODE%", randomAlphanumeric(7)) + .replaceAll("%PROSECUTION_AUTHORITY_OU_CODE%", randomAlphanumeric(7)) + .replaceAll("%REGISTER_DATE%", registerDate.toString()) + .replaceAll("%HEARING_ID%", hearingId.toString()) + .replaceAll("%HEARING_DATE%", registerDate.minusHours(1).toString()); + return createReader(new StringReader(body)).readObject(); + } + + /** + * A V1 (core-namespace) informant-register document request. The pre-migration shape carries a flat + * {@code verdictCode} on the offence rather than a structured verdict object. + */ + private JsonObject v1DocumentRequest(final UUID prosecutionAuthorityId, final ZonedDateTime registerDate, final UUID hearingId) { + final JsonObject offence = createObjectBuilder() + .add("offenceCode", "PS90010") + .add("orderIndex", 1) + .add("offenceTitle", "Theft") + .add("pleaValue", "NOT_GUILTY") + .add("verdictCode", "G") + .build(); + + final JsonObject hearingVenue = createObjectBuilder() + .add("courtHouse", "Crown Court") + .add("ljaName", "LJA") + .add("courtSessions", createArrayBuilder() + .add(createObjectBuilder() + .add("courtRoom", "Room 1") + .add("hearingStartTime", registerDate.minusHours(1).toString()) + .add("defendants", createArrayBuilder() + .add(createObjectBuilder() + .add("name", "John Smith") + .add("address1", "1 High St") + .add("firstName", "John") + .add("lastName", "Smith") + .add("prosecutionCasesOrApplications", createArrayBuilder() + .add(createObjectBuilder() + .add("caseOrApplicationReference", "TFL123") + .add("offences", createArrayBuilder().add(offence)))))))) + .build(); + + return createObjectBuilder() + .add("prosecutionAuthorityId", prosecutionAuthorityId.toString()) + .add("registerDate", registerDate.toString()) + .add("hearingDate", registerDate.minusHours(1).toString()) + .add("hearingId", hearingId.toString()) + .add("prosecutionAuthorityCode", randomAlphanumeric(7)) + .add("fileName", "informant-register.csv") + .add("hearingVenue", hearingVenue) + .build(); + } +} diff --git a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java index c713251c6..dec84eb1c 100644 --- a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java +++ b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/helper/InformantRegisterDocumentRequestHelper.java @@ -109,6 +109,20 @@ public void verifyInformantRegisterIsNotified(final UUID prosecutionAuthorityId) )); } + /** + * Verifies that a register for the given prosecuting authority has progressed out of RECORDED — i.e. an + * informant-register-generated(-v2) event was consumed by the listener and advanced the projection. This is + * asserted as absence from the RECORDED list (rather than presence in GENERATED) because the generate flow can + * progress further to NOTIFIED asynchronously: the processor reacts to the generated event by sending a + * notify-informant-register command. Absence-from-RECORDED is stable whether the register lands on GENERATED or + * NOTIFIED. + */ + public void verifyInformantRegisterNoLongerRecorded(final UUID prosecutionAuthorityId) { + getInformantRegisterDocumentRequests(RECORDED.name(), + withJsonPath("$.informantRegisterDocumentRequests[*].prosecutionAuthorityId", not(hasItem(prosecutionAuthorityId.toString()))) + ); + } + public void verifyProsecutorResultsContainVerdict(final String ouCode, final LocalDate startDate, final String expectedVerdictCode) { pollWithDefaults(requestParams(getReadUrl(StringUtils.join("/prosecutor/", ouCode, "?startDate=", startDate.toString())), "application/vnd.results.prosecutor-results+json") diff --git a/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json index 096930c91..cf7340b13 100644 --- a/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json +++ b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterDocumentRequest.json @@ -14,11 +14,11 @@ }, "hearingId": { "description": "The hearing id of the hearing shared", - "type": "string" + "$ref": "http://justice.gov.uk/core/courts/courtsDefinitions.json#/definitions/uuid" }, "prosecutionAuthorityId": { "description": "The prosecuting authority reference data identifier", - "type": "string" + "$ref": "http://justice.gov.uk/core/courts/courtsDefinitions.json#/definitions/uuid" }, "prosecutionAuthorityCode": { "description": "The prosecuting authority reference data code", @@ -51,7 +51,7 @@ }, "groupId": { "description": "The group id of the group cases", - "type": "string" + "$ref": "http://justice.gov.uk/core/courts/courtsDefinitions.json#/definitions/uuid" } }, "required": [ diff --git a/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json index df69aad5f..e7062d4e3 100644 --- a/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json +++ b/results-json/src/main/resources/json/schema/informantRegisterDocument/informantRegisterResultData.json @@ -9,8 +9,7 @@ "type": "string" }, "nextHearingDate": { - "type": "string", - "format": "date-time" + "type": "string" }, "nextCourtLocation": { "type": "string" @@ -22,12 +21,10 @@ "type": "string" }, "durationStartDate": { - "type": "string", - "format": "date-time" + "type": "string" }, "durationEndDate": { - "type": "string", - "format": "date-time" + "type": "string" }, "secondaryDurationValue": { "type": "string" diff --git a/results-json/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json index eae865a4b..41dc240bd 100644 --- a/results-json/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json +++ b/results-json/src/main/resources/json/schema/informantRegisterDocument/prosecutorResult.json @@ -5,16 +5,14 @@ "type": "object", "properties": { "startDate": { - "type": "string", - "format": "date" + "$ref": "http://justice.gov.uk/core/courts/courtsDefinitions.json#/definitions/datePattern" }, "endDate": { - "type": "string", - "format": "date" + "$ref": "http://justice.gov.uk/core/courts/courtsDefinitions.json#/definitions/datePattern" }, "prosecutionAuthorityId": { "description": "The prosecuting authority reference data identifier", - "type": "string" + "$ref": "http://justice.gov.uk/core/courts/courtsDefinitions.json#/definitions/uuid" }, "prosecutionAuthorityCode": { "description": "The prosecuting authority reference data code", diff --git a/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java b/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java index b4f5b19f8..5b3909300 100644 --- a/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java +++ b/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryView.java @@ -4,14 +4,14 @@ import static org.apache.commons.collections.CollectionUtils.isNotEmpty; import static uk.gov.justice.services.messaging.JsonEnvelope.envelopeFrom; import static uk.gov.justice.services.messaging.JsonObjects.getString; -import static uk.gov.moj.cpp.results.domain.informant.model.ProsecutorResult.prosecutorResult; +import static uk.gov.justice.results.courts.informantRegisterDocument.ProsecutorResult.prosecutorResult; 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.messaging.JsonEnvelope; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; -import uk.gov.moj.cpp.results.domain.informant.model.ProsecutorResult; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest; +import uk.gov.justice.results.courts.informantRegisterDocument.ProsecutorResult; import uk.gov.moj.cpp.results.persist.InformantRegisterRepository; import uk.gov.moj.cpp.results.persist.entity.InformantRegisterEntity; diff --git a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryViewTest.java b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryViewTest.java index 291607593..6ab067fe8 100644 --- a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryViewTest.java +++ b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/ProsecutorResultsQueryViewTest.java @@ -15,13 +15,13 @@ import static org.mockito.Mockito.when; import static uk.gov.justice.services.messaging.Envelope.metadataBuilder; import static uk.gov.justice.services.messaging.JsonEnvelope.envelopeFrom; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant.informantRegisterDefendant; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing.informantRegisterHearing; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue.informantRegisterHearingVenue; -import static uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence.informantRegisterOffence; -import static uk.gov.moj.cpp.results.domain.informant.model.Verdict.verdict; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant.informantRegisterDefendant; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterHearing.informantRegisterHearing; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterHearingVenue.informantRegisterHearingVenue; +import static uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterOffence.informantRegisterOffence; +import static uk.gov.justice.results.courts.informantRegisterDocument.Verdict.verdict; import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter; import uk.gov.justice.services.common.converter.ObjectToJsonObjectConverter; @@ -29,13 +29,13 @@ import uk.gov.justice.services.common.converter.jackson.ObjectMapperProducer; import uk.gov.justice.services.messaging.JsonEnvelope; import uk.gov.justice.services.messaging.MetadataBuilder; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterCaseOrApplication; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDefendant; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterDocumentRequest; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearing; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterHearingVenue; -import uk.gov.moj.cpp.results.domain.informant.model.InformantRegisterOffence; -import uk.gov.moj.cpp.results.domain.informant.model.Verdict; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterCaseOrApplication; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterHearing; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterHearingVenue; +import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterOffence; +import uk.gov.justice.results.courts.informantRegisterDocument.Verdict; import uk.gov.moj.cpp.results.persist.InformantRegisterRepository; import uk.gov.moj.cpp.results.persist.entity.InformantRegisterEntity; From 0d777456307a3bb8209afcab51165e00fb5c8dfb Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Wed, 17 Jun 2026 23:45:49 +0100 Subject: [PATCH 09/22] CIMD-3915 : verdict implementation --- .../handler/InformantRegisterHandler.java | 88 +++++++++- .../handler/InformantRegisterHandlerTest.java | 153 ++++++++++++++++++ .../it/InformantRegisterEventFlowsIT.java | 86 +++++++++- 3 files changed, 316 insertions(+), 11 deletions(-) diff --git a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java index 556155c87..18ea12c61 100644 --- a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java +++ b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java @@ -14,6 +14,7 @@ import static uk.gov.moj.cpp.results.command.util.DefendantMapper.getDefendants; import uk.gov.justice.core.courts.ProsecutionCase; +import uk.gov.justice.results.courts.InformantRegisterGenerated; import uk.gov.justice.results.courts.InformantRegisterGeneratedV2; import uk.gov.justice.results.courts.InformantRegisterRecordedV2; import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant; @@ -36,6 +37,7 @@ import uk.gov.moj.cpp.results.command.service.ProgressionQueryService; import uk.gov.moj.cpp.results.domain.aggregate.ProsecutionAuthorityAggregate; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -62,6 +64,12 @@ public class InformantRegisterHandler { private static final String FIELD_REQUEST_STATUS = "requestStatus"; private static final String FIELD_REGISTER_DATE = "registerDate"; private static final String FIELD_PROSECUTION_AUTHORITY_CODE = "prosecutionAuthorityCode"; + private static final String FIELD_HEARING_VENUE = "hearingVenue"; + private static final String FIELD_COURT_SESSIONS = "courtSessions"; + private static final String FIELD_DEFENDANTS = "defendants"; + private static final String FIELD_CASES_OR_APPLICATIONS = "prosecutionCasesOrApplications"; + private static final String FIELD_OFFENCES = "offences"; + private static final String FIELD_VERDICT_CODE = "verdictCode"; @Inject private EventSource eventSource; @@ -162,21 +170,87 @@ public void handleNotifyInformantRegister(final Envelope informantRegisterRequest, final Envelope jsonEnvelope, final boolean systemGenerated) { try { final EventStream eventStream = eventSource.getStreamById(informantRegisterId); - final List informantRegisterDocumentRequests = informantRegisterRequest.stream().map(informantRegister -> stringToJsonObjectConverter.convert(informantRegister.getString((FIELD_PAYLOAD)))) - .map(informantRegister -> jsonObjectToObjectConverter.convert(informantRegister, InformantRegisterDocumentRequest.class)) + + final List payloads = informantRegisterRequest.stream() + .map(informantRegister -> stringToJsonObjectConverter.convert(informantRegister.getString(FIELD_PAYLOAD))) + .toList(); + + final List v1DocumentRequests = payloads.stream() + .filter(this::isV1InformantRegisterPayload) + .map(payload -> jsonObjectToObjectConverter.convert(payload, uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest.class)) .toList(); - final Stream events = Stream.of(InformantRegisterGeneratedV2.informantRegisterGeneratedV2() - .withInformantRegisterDocumentRequests(informantRegisterDocumentRequests) - .withSystemGenerated(systemGenerated) - .build()); + final List v2DocumentRequests = payloads.stream() + .filter(payload -> !isV1InformantRegisterPayload(payload)) + .map(payload -> jsonObjectToObjectConverter.convert(payload, InformantRegisterDocumentRequest.class)) + .toList(); - appendEventsToStream(jsonEnvelope, eventStream, events); + LOGGER.debug("Generating informant register for stream {}: {} V1 request(s), {} V2 request(s)", + informantRegisterId, v1DocumentRequests.size(), v2DocumentRequests.size()); + + final List events = new ArrayList<>(); + if (isNotEmpty(v1DocumentRequests)) { + events.add(InformantRegisterGenerated.informantRegisterGenerated() + .withInformantRegisterDocumentRequests(v1DocumentRequests) + .withSystemGenerated(systemGenerated) + .build()); + } + if (isNotEmpty(v2DocumentRequests)) { + events.add(InformantRegisterGeneratedV2.informantRegisterGeneratedV2() + .withInformantRegisterDocumentRequests(v2DocumentRequests) + .withSystemGenerated(systemGenerated) + .build()); + } + + appendEventsToStream(jsonEnvelope, eventStream, events.stream()); } catch (EventStreamException e) { LOGGER.error("Generate informant register stream exception -->>", e); } } + /** + * Classifies a queried register payload as V1 or V2 so it can be converted to the matching document-request + * type and emitted as the matching generated event. + * + *

This relies on an invariant of the record side: each stored payload is a whole document request recorded + * via either {@code results.event.informant-register-recorded} (V1, flat {@code verdictCode} string on the + * offence) or {@code results.event.informant-register-recorded-v2} (V2, structured {@code verdict} object), and + * is validated against a single schema version at record time. A single stored payload therefore never mixes + * offence shapes — it is uniformly V1 or uniformly V2. + * + *

Given that invariant, a payload is treated as V1 only when it positively carries the legacy top-level + * {@code verdictCode} on an offence; structured-verdict payloads and payloads with no offences are treated as + * V2, so a structured verdict is never silently dropped. + */ + private boolean isV1InformantRegisterPayload(final JsonObject payload) { + return offences(payload).anyMatch(offence -> offence.containsKey(FIELD_VERDICT_CODE)); + } + + private Stream offences(final JsonObject payload) { + final JsonObject hearingVenue = objectOrNull(payload, FIELD_HEARING_VENUE); + if (hearingVenue == null) { + return Stream.empty(); + } + return arrayObjects(hearingVenue, FIELD_COURT_SESSIONS) + .flatMap(courtSession -> arrayObjects(courtSession, FIELD_DEFENDANTS)) + .flatMap(defendant -> arrayObjects(defendant, FIELD_CASES_OR_APPLICATIONS)) + .flatMap(caseOrApplication -> arrayObjects(caseOrApplication, FIELD_OFFENCES)); + } + + private JsonObject objectOrNull(final JsonObject parent, final String field) { + if (!parent.containsKey(field) || parent.get(field).getValueType() != JsonValue.ValueType.OBJECT) { + return null; + } + return parent.getJsonObject(field); + } + + private Stream arrayObjects(final JsonObject parent, final String field) { + if (!parent.containsKey(field) || parent.get(field).getValueType() != JsonValue.ValueType.ARRAY) { + return Stream.empty(); + } + return parent.getJsonArray(field).getValuesAs(JsonObject.class).stream(); + } + private Map> getInformantRegisterDocumentRequests(final Envelope envelope) { final List informantRegisterDocumentRequests = queryInformantRegistersByStatus(envelope); diff --git a/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java b/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java index 1f022c87c..48a31790f 100644 --- a/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java +++ b/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java @@ -8,6 +8,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; @@ -329,6 +330,77 @@ public void generateInformantRegister() throws EventStreamException { } + @Test + public void generateInformantRegister_withV1Payload_shouldEmitV1Event() throws EventStreamException { + final Envelope generateInformantRegisterEnvelope = prepareEnvelope(); + final JsonEnvelope queryEnvelope = mock(JsonEnvelope.class); + final JsonObject queryResponse = queryResponseWith(buildV1PayloadJson()); + + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(queryEnvelope.payloadAsJsonObject()).thenReturn(queryResponse); + when(requester.request(any(Envelope.class))).thenReturn(queryEnvelope); + + informantRegisterHandler.handleGenerateInformantRegister(generateInformantRegisterEnvelope); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("results.event.informant-register-generated"), + JsonEnvelopePayloadMatcher.payload().isJson(allOf( + withJsonPath("$.informantRegisterDocumentRequests.length()", is(1)), + withJsonPath("$.informantRegisterDocumentRequests[0].hearingVenue.courtSessions[0]." + + "defendants[0].prosecutionCasesOrApplications[0].offences[0].verdictCode", is("GUILTY")) + )) + ) + )); + } + + @Test + public void generateInformantRegister_withV2Payload_shouldEmitV2Event() throws EventStreamException { + final Envelope generateInformantRegisterEnvelope = prepareEnvelope(); + final JsonEnvelope queryEnvelope = mock(JsonEnvelope.class); + final JsonObject queryResponse = queryResponseWith(buildV2PayloadJson()); + + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(queryEnvelope.payloadAsJsonObject()).thenReturn(queryResponse); + when(requester.request(any(Envelope.class))).thenReturn(queryEnvelope); + + informantRegisterHandler.handleGenerateInformantRegister(generateInformantRegisterEnvelope); + + final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); + assertThat(envelopeStream, streamContaining( + jsonEnvelope( + metadata().withName("results.event.informant-register-generated-v2"), + JsonEnvelopePayloadMatcher.payload().isJson(allOf( + withJsonPath("$.informantRegisterDocumentRequests.length()", is(1)), + withJsonPath("$.informantRegisterDocumentRequests[0].hearingVenue.courtSessions[0]." + + "defendants[0].prosecutionCasesOrApplications[0].offences[0].verdict.verdictCode", is("GUILTY")) + )) + ) + )); + } + + @Test + public void generateInformantRegister_withMixedV1AndV2Payloads_shouldEmitBothEvents() throws EventStreamException { + final Envelope generateInformantRegisterEnvelope = prepareEnvelope(); + final JsonEnvelope queryEnvelope = mock(JsonEnvelope.class); + final JsonObject queryResponse = queryResponseWith(buildV1PayloadJson(), buildV2PayloadJson()); + + when(eventSource.getStreamById(any())).thenReturn(eventStream); + when(queryEnvelope.payloadAsJsonObject()).thenReturn(queryResponse); + when(requester.request(any(Envelope.class))).thenReturn(queryEnvelope); + + informantRegisterHandler.handleGenerateInformantRegister(generateInformantRegisterEnvelope); + + final List eventNames = verifyAppendAndGetArgumentFrom(eventStream) + .map(jsonEnvelope -> jsonEnvelope.metadata().name()) + .toList(); + + assertThat(eventNames, containsInAnyOrder( + "results.event.informant-register-generated", + "results.event.informant-register-generated-v2")); + } + @Test public void notifyInformantRegister() throws EventStreamException { final UUID materialId = randomUUID(); @@ -443,6 +515,87 @@ private void verifyInformantRegisterDocumentRequestHandlerResults() throws Event ); } + private JsonObject queryResponseWith(final String... payloads) { + final JsonArrayBuilder arrayBuilder = createArrayBuilder(); + for (final String payload : payloads) { + arrayBuilder.add(createObjectBuilder() + .add("prosecutionAuthorityId", PROSECUTION_AUTHORITY_ID.toString()) + .add("registerDate", REGISTER_DATE.toString()) + .add("payload", payload) + .build()); + } + return createObjectBuilder().add("informantRegisterDocumentRequests", arrayBuilder.build()).build(); + } + + private String buildV1PayloadJson() { + final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterOffence offence = + uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterOffence.informantRegisterOffence() + .withOffenceCode("offenceCode_V1") + .withOffenceTitle("offenceTitle_V1") + .withOrderIndex(1) + .withVerdictCode("GUILTY") + .build(); + final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterCaseOrApplication caseOrApplication = + uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication() + .withCaseOrApplicationReference("caseURN_V1") + .withOffences(List.of(offence)) + .build(); + final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDefendant defendant = + uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDefendant.informantRegisterDefendant() + .withFirstName("firstName_V1") + .withProsecutionCasesOrApplications(List.of(caseOrApplication)) + .build(); + final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearing hearing = + uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearing.informantRegisterHearing() + .withCourtRoom("courtroom_V1") + .withDefendants(List.of(defendant)) + .build(); + final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearingVenue hearingVenue = + uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearingVenue.informantRegisterHearingVenue() + .withCourtHouse("courtHouse_V1") + .withCourtSessions(List.of(hearing)) + .build(); + final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest request = + uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest() + .withProsecutionAuthorityId(PROSECUTION_AUTHORITY_ID) + .withRegisterDate(REGISTER_DATE) + .withHearingVenue(hearingVenue) + .build(); + return objectToJsonObjectConverter.convert(request).toString(); + } + + private String buildV2PayloadJson() { + final InformantRegisterOffence offence = InformantRegisterOffence.informantRegisterOffence() + .withOffenceCode("offenceCode_V2") + .withOffenceTitle("offenceTitle_V2") + .withOrderIndex(1) + .withVerdict(Verdict.verdict().withVerdictCode("GUILTY").build()) + .build(); + final InformantRegisterCaseOrApplication caseOrApplication = + InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication() + .withCaseOrApplicationReference("caseURN_V2") + .withOffences(List.of(offence)) + .build(); + final InformantRegisterDefendant defendant = InformantRegisterDefendant.informantRegisterDefendant() + .withFirstName("firstName_V2") + .withProsecutionCasesOrApplications(List.of(caseOrApplication)) + .build(); + final InformantRegisterHearing hearing = InformantRegisterHearing.informantRegisterHearing() + .withCourtRoom("courtroom_V2") + .withDefendants(List.of(defendant)) + .build(); + final InformantRegisterHearingVenue hearingVenue = InformantRegisterHearingVenue.informantRegisterHearingVenue() + .withCourtHouse("courtHouse_V2") + .withCourtSessions(List.of(hearing)) + .build(); + final InformantRegisterDocumentRequest request = informantRegisterDocumentRequest() + .withProsecutionAuthorityId(PROSECUTION_AUTHORITY_ID) + .withRegisterDate(REGISTER_DATE) + .withHearingVenue(hearingVenue) + .build(); + return objectToJsonObjectConverter.convert(request).toString(); + } + private Envelope buildEnvelope() { return buildEnvelope(null, false, false); } diff --git a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java index d767ffb1d..54e207636 100644 --- a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java +++ b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java @@ -4,12 +4,19 @@ import static java.time.ZonedDateTime.now; import static java.util.UUID.randomUUID; import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric; +import static org.apache.http.HttpStatus.SC_ACCEPTED; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static uk.gov.justice.services.messaging.JsonEnvelope.metadataBuilder; import static uk.gov.justice.services.messaging.JsonObjects.createArrayBuilder; import static uk.gov.justice.services.messaging.JsonObjects.createObjectBuilder; import static uk.gov.justice.services.messaging.JsonObjects.createReader; +import static uk.gov.moj.cpp.results.it.helper.InformantRegisterDocumentRequestHelper.getWriteUrl; +import static uk.gov.moj.cpp.results.it.helper.RestHelper.postCommand; import static uk.gov.moj.cpp.results.it.utils.FileUtil.getPayload; import static uk.gov.moj.cpp.results.it.utils.QueueUtil.privateEvents; +import static uk.gov.moj.cpp.results.it.utils.QueueUtil.retrieveMessage; import static uk.gov.moj.cpp.results.it.utils.QueueUtil.sendMessage; import static uk.gov.moj.cpp.results.it.utils.WireMockStubUtils.setupUsersGroupQueryStub; @@ -18,13 +25,18 @@ import java.io.StringReader; import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; import java.util.Random; import java.util.UUID; import javax.jms.JMSException; +import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.json.JsonObject; +import io.restassured.path.json.JsonPath; +import io.restassured.response.Response; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -41,10 +53,14 @@ *

  • {@code results.event.informant-register-generated-v2} (V2) advances a RECORDED register past RECORDED
  • * * - *

    The current command path only ever emits the V2 events, so the V1 events are produced here synthetically to - * confirm their listener handlers still resolve and project correctly (this is the listener-resolution guarantee the - * V2/generated-class consolidation depends on). The {@code generated} events only transition existing RECORDED - * registers, so each generate test first injects the matching {@code recorded} event.

    + *

    The {@code recorded} flows are produced here synthetically (published straight onto the topic) to confirm the + * listener handlers resolve and project correctly. Since CIMD-3915 the command path emits the V1 generated event too + * (not only V2): {@code generate-informant-register} splits the queried registers by version and emits + * {@code informant-register-generated} for legacy (flat {@code verdictCode}) payloads and + * {@code informant-register-generated-v2} for structured-verdict payloads. The + * {@link #generateCommandWithMixedV1AndV2RecordedRegistersEmitsBothGeneratedEvents()} test exercises that split + * end-to-end through the real command. The {@code generated} events only transition existing RECORDED registers, so + * each generate test first injects the matching {@code recorded} event.

    */ public class InformantRegisterEventFlowsIT { @@ -56,6 +72,12 @@ public class InformantRegisterEventFlowsIT { private static final String V2_DOCUMENT_REQUEST_RESOURCE = "json/informant-register/results.add-informant-register-document-request.json"; + private static final String GENERATE_COMMAND_MEDIA_TYPE = "application/vnd.results.generate-informant-register+json"; + + // Per-poll timeout while draining the topic of generated events after the command. Long enough to cover the + // async command -> event-store -> topic hop, short enough that the drain loop terminates once the topic is quiet. + private static final long DRAIN_TIMEOUT_MILLIS = 10000; + private static final Random RANDOM = new Random(); private InformantRegisterDocumentRequestHelper helper; @@ -125,6 +147,62 @@ public void generatedEventShouldAdvanceRecordedRegister() { helper.verifyInformantRegisterNoLongerRecorded(prosecutionAuthorityId); } + /** + * End-to-end exercise of the CIMD-3915 version split: a single prosecution authority + register date holding both + * a legacy V1 (flat {@code verdictCode}) and a new V2 (structured {@code verdict}) RECORDED register. The real + * {@code generate-informant-register} command groups them onto one stream and must emit BOTH a V1 + * {@code informant-register-generated} (preserving {@code verdictCode}) and a V2 {@code informant-register-generated-v2}. + * Before the fix the command emitted only the V2 event for every register, dropping the V1 {@code verdictCode}. + */ + @Test + public void generateCommandWithMixedV1AndV2RecordedRegistersEmitsBothGeneratedEvents() { + final UUID prosecutionAuthorityId = randomUUID(); + final ZonedDateTime registerDate = now(UTC); + + // Consumers must be live before the command is issued so the emitted generated events are captured. + final MessageConsumer v1GeneratedConsumer = privateEvents.createConsumer(EVENT_GENERATED); + final MessageConsumer v2GeneratedConsumer = privateEvents.createConsumer(EVENT_GENERATED_V2); + + final JsonObject v1Doc = v1DocumentRequest(prosecutionAuthorityId, registerDate, randomUUID()); + final JsonObject v2Doc = v2DocumentRequest(prosecutionAuthorityId, registerDate, randomUUID()); + publishRecorded(EVENT_RECORDED, prosecutionAuthorityId, v1Doc); + publishRecorded(EVENT_RECORDED_V2, prosecutionAuthorityId, v2Doc); + helper.verifyInformantRegisterRequestsExists(prosecutionAuthorityId); + + final Response response = postCommand(getWriteUrl("/informant-register/generate"), GENERATE_COMMAND_MEDIA_TYPE, ""); + assertThat(response.getStatusCode(), equalTo(SC_ACCEPTED)); + + final List v1Generated = drainGeneratedEventsFor(v1GeneratedConsumer, prosecutionAuthorityId); + final List v2Generated = drainGeneratedEventsFor(v2GeneratedConsumer, prosecutionAuthorityId); + + assertThat("expected a V1 informant-register-generated event for the V1-recorded register", + v1Generated.size(), greaterThanOrEqualTo(1)); + assertThat("V1 generated event must preserve the legacy flat verdictCode", + v1Generated.get(0).getString("informantRegisterDocumentRequests[0].hearingVenue.courtSessions[0]" + + ".defendants[0].prosecutionCasesOrApplications[0].offences[0].verdictCode"), + equalTo("G")); + assertThat("expected a V2 informant-register-generated-v2 event for the V2-recorded register", + v2Generated.size(), greaterThanOrEqualTo(1)); + } + + /** + * Drains all currently-available generated events from the consumer (the unscoped generate command advances every + * RECORDED register in the view-store) and keeps only those belonging to the given prosecution authority, so + * registers seeded by other tests do not cause false positives or negatives. + */ + private List drainGeneratedEventsFor(final MessageConsumer consumer, final UUID prosecutionAuthorityId) { + final List matches = new ArrayList<>(); + JsonPath message = retrieveMessage(consumer, DRAIN_TIMEOUT_MILLIS); + while (message != null) { + final String paId = message.getString("informantRegisterDocumentRequests[0].prosecutionAuthorityId"); + if (prosecutionAuthorityId.toString().equals(paId)) { + matches.add(message); + } + message = retrieveMessage(consumer, DRAIN_TIMEOUT_MILLIS); + } + return matches; + } + private void publishRecorded(final String eventName, final UUID prosecutionAuthorityId, final JsonObject documentRequest) { final JsonObject payload = createObjectBuilder() .add("prosecutionAuthorityId", prosecutionAuthorityId.toString()) From b6582173541d1840846013575c773bba0be7ef82 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Thu, 18 Jun 2026 14:05:54 +0100 Subject: [PATCH 10/22] Revert "CIMD-3915 : verdict implementation" This reverts commit 0d777456307a3bb8209afcab51165e00fb5c8dfb. --- .../handler/InformantRegisterHandler.java | 88 +--------- .../handler/InformantRegisterHandlerTest.java | 153 ------------------ .../it/InformantRegisterEventFlowsIT.java | 86 +--------- 3 files changed, 11 insertions(+), 316 deletions(-) diff --git a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java index 18ea12c61..556155c87 100644 --- a/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java +++ b/results-command/results-command-handler/src/main/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandler.java @@ -14,7 +14,6 @@ import static uk.gov.moj.cpp.results.command.util.DefendantMapper.getDefendants; import uk.gov.justice.core.courts.ProsecutionCase; -import uk.gov.justice.results.courts.InformantRegisterGenerated; import uk.gov.justice.results.courts.InformantRegisterGeneratedV2; import uk.gov.justice.results.courts.InformantRegisterRecordedV2; import uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDefendant; @@ -37,7 +36,6 @@ import uk.gov.moj.cpp.results.command.service.ProgressionQueryService; import uk.gov.moj.cpp.results.domain.aggregate.ProsecutionAuthorityAggregate; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -64,12 +62,6 @@ public class InformantRegisterHandler { private static final String FIELD_REQUEST_STATUS = "requestStatus"; private static final String FIELD_REGISTER_DATE = "registerDate"; private static final String FIELD_PROSECUTION_AUTHORITY_CODE = "prosecutionAuthorityCode"; - private static final String FIELD_HEARING_VENUE = "hearingVenue"; - private static final String FIELD_COURT_SESSIONS = "courtSessions"; - private static final String FIELD_DEFENDANTS = "defendants"; - private static final String FIELD_CASES_OR_APPLICATIONS = "prosecutionCasesOrApplications"; - private static final String FIELD_OFFENCES = "offences"; - private static final String FIELD_VERDICT_CODE = "verdictCode"; @Inject private EventSource eventSource; @@ -170,87 +162,21 @@ public void handleNotifyInformantRegister(final Envelope informantRegisterRequest, final Envelope jsonEnvelope, final boolean systemGenerated) { try { final EventStream eventStream = eventSource.getStreamById(informantRegisterId); - - final List payloads = informantRegisterRequest.stream() - .map(informantRegister -> stringToJsonObjectConverter.convert(informantRegister.getString(FIELD_PAYLOAD))) - .toList(); - - final List v1DocumentRequests = payloads.stream() - .filter(this::isV1InformantRegisterPayload) - .map(payload -> jsonObjectToObjectConverter.convert(payload, uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest.class)) + final List informantRegisterDocumentRequests = informantRegisterRequest.stream().map(informantRegister -> stringToJsonObjectConverter.convert(informantRegister.getString((FIELD_PAYLOAD)))) + .map(informantRegister -> jsonObjectToObjectConverter.convert(informantRegister, InformantRegisterDocumentRequest.class)) .toList(); - final List v2DocumentRequests = payloads.stream() - .filter(payload -> !isV1InformantRegisterPayload(payload)) - .map(payload -> jsonObjectToObjectConverter.convert(payload, InformantRegisterDocumentRequest.class)) - .toList(); + final Stream events = Stream.of(InformantRegisterGeneratedV2.informantRegisterGeneratedV2() + .withInformantRegisterDocumentRequests(informantRegisterDocumentRequests) + .withSystemGenerated(systemGenerated) + .build()); - LOGGER.debug("Generating informant register for stream {}: {} V1 request(s), {} V2 request(s)", - informantRegisterId, v1DocumentRequests.size(), v2DocumentRequests.size()); - - final List events = new ArrayList<>(); - if (isNotEmpty(v1DocumentRequests)) { - events.add(InformantRegisterGenerated.informantRegisterGenerated() - .withInformantRegisterDocumentRequests(v1DocumentRequests) - .withSystemGenerated(systemGenerated) - .build()); - } - if (isNotEmpty(v2DocumentRequests)) { - events.add(InformantRegisterGeneratedV2.informantRegisterGeneratedV2() - .withInformantRegisterDocumentRequests(v2DocumentRequests) - .withSystemGenerated(systemGenerated) - .build()); - } - - appendEventsToStream(jsonEnvelope, eventStream, events.stream()); + appendEventsToStream(jsonEnvelope, eventStream, events); } catch (EventStreamException e) { LOGGER.error("Generate informant register stream exception -->>", e); } } - /** - * Classifies a queried register payload as V1 or V2 so it can be converted to the matching document-request - * type and emitted as the matching generated event. - * - *

    This relies on an invariant of the record side: each stored payload is a whole document request recorded - * via either {@code results.event.informant-register-recorded} (V1, flat {@code verdictCode} string on the - * offence) or {@code results.event.informant-register-recorded-v2} (V2, structured {@code verdict} object), and - * is validated against a single schema version at record time. A single stored payload therefore never mixes - * offence shapes — it is uniformly V1 or uniformly V2. - * - *

    Given that invariant, a payload is treated as V1 only when it positively carries the legacy top-level - * {@code verdictCode} on an offence; structured-verdict payloads and payloads with no offences are treated as - * V2, so a structured verdict is never silently dropped. - */ - private boolean isV1InformantRegisterPayload(final JsonObject payload) { - return offences(payload).anyMatch(offence -> offence.containsKey(FIELD_VERDICT_CODE)); - } - - private Stream offences(final JsonObject payload) { - final JsonObject hearingVenue = objectOrNull(payload, FIELD_HEARING_VENUE); - if (hearingVenue == null) { - return Stream.empty(); - } - return arrayObjects(hearingVenue, FIELD_COURT_SESSIONS) - .flatMap(courtSession -> arrayObjects(courtSession, FIELD_DEFENDANTS)) - .flatMap(defendant -> arrayObjects(defendant, FIELD_CASES_OR_APPLICATIONS)) - .flatMap(caseOrApplication -> arrayObjects(caseOrApplication, FIELD_OFFENCES)); - } - - private JsonObject objectOrNull(final JsonObject parent, final String field) { - if (!parent.containsKey(field) || parent.get(field).getValueType() != JsonValue.ValueType.OBJECT) { - return null; - } - return parent.getJsonObject(field); - } - - private Stream arrayObjects(final JsonObject parent, final String field) { - if (!parent.containsKey(field) || parent.get(field).getValueType() != JsonValue.ValueType.ARRAY) { - return Stream.empty(); - } - return parent.getJsonArray(field).getValuesAs(JsonObject.class).stream(); - } - private Map> getInformantRegisterDocumentRequests(final Envelope envelope) { final List informantRegisterDocumentRequests = queryInformantRegistersByStatus(envelope); diff --git a/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java b/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java index 48a31790f..1f022c87c 100644 --- a/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java +++ b/results-command/results-command-handler/src/test/java/uk/gov/moj/cpp/results/command/handler/InformantRegisterHandlerTest.java @@ -8,7 +8,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.anyOf; -import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; @@ -330,77 +329,6 @@ public void generateInformantRegister() throws EventStreamException { } - @Test - public void generateInformantRegister_withV1Payload_shouldEmitV1Event() throws EventStreamException { - final Envelope generateInformantRegisterEnvelope = prepareEnvelope(); - final JsonEnvelope queryEnvelope = mock(JsonEnvelope.class); - final JsonObject queryResponse = queryResponseWith(buildV1PayloadJson()); - - when(eventSource.getStreamById(any())).thenReturn(eventStream); - when(queryEnvelope.payloadAsJsonObject()).thenReturn(queryResponse); - when(requester.request(any(Envelope.class))).thenReturn(queryEnvelope); - - informantRegisterHandler.handleGenerateInformantRegister(generateInformantRegisterEnvelope); - - final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); - assertThat(envelopeStream, streamContaining( - jsonEnvelope( - metadata().withName("results.event.informant-register-generated"), - JsonEnvelopePayloadMatcher.payload().isJson(allOf( - withJsonPath("$.informantRegisterDocumentRequests.length()", is(1)), - withJsonPath("$.informantRegisterDocumentRequests[0].hearingVenue.courtSessions[0]." + - "defendants[0].prosecutionCasesOrApplications[0].offences[0].verdictCode", is("GUILTY")) - )) - ) - )); - } - - @Test - public void generateInformantRegister_withV2Payload_shouldEmitV2Event() throws EventStreamException { - final Envelope generateInformantRegisterEnvelope = prepareEnvelope(); - final JsonEnvelope queryEnvelope = mock(JsonEnvelope.class); - final JsonObject queryResponse = queryResponseWith(buildV2PayloadJson()); - - when(eventSource.getStreamById(any())).thenReturn(eventStream); - when(queryEnvelope.payloadAsJsonObject()).thenReturn(queryResponse); - when(requester.request(any(Envelope.class))).thenReturn(queryEnvelope); - - informantRegisterHandler.handleGenerateInformantRegister(generateInformantRegisterEnvelope); - - final Stream envelopeStream = verifyAppendAndGetArgumentFrom(eventStream); - assertThat(envelopeStream, streamContaining( - jsonEnvelope( - metadata().withName("results.event.informant-register-generated-v2"), - JsonEnvelopePayloadMatcher.payload().isJson(allOf( - withJsonPath("$.informantRegisterDocumentRequests.length()", is(1)), - withJsonPath("$.informantRegisterDocumentRequests[0].hearingVenue.courtSessions[0]." + - "defendants[0].prosecutionCasesOrApplications[0].offences[0].verdict.verdictCode", is("GUILTY")) - )) - ) - )); - } - - @Test - public void generateInformantRegister_withMixedV1AndV2Payloads_shouldEmitBothEvents() throws EventStreamException { - final Envelope generateInformantRegisterEnvelope = prepareEnvelope(); - final JsonEnvelope queryEnvelope = mock(JsonEnvelope.class); - final JsonObject queryResponse = queryResponseWith(buildV1PayloadJson(), buildV2PayloadJson()); - - when(eventSource.getStreamById(any())).thenReturn(eventStream); - when(queryEnvelope.payloadAsJsonObject()).thenReturn(queryResponse); - when(requester.request(any(Envelope.class))).thenReturn(queryEnvelope); - - informantRegisterHandler.handleGenerateInformantRegister(generateInformantRegisterEnvelope); - - final List eventNames = verifyAppendAndGetArgumentFrom(eventStream) - .map(jsonEnvelope -> jsonEnvelope.metadata().name()) - .toList(); - - assertThat(eventNames, containsInAnyOrder( - "results.event.informant-register-generated", - "results.event.informant-register-generated-v2")); - } - @Test public void notifyInformantRegister() throws EventStreamException { final UUID materialId = randomUUID(); @@ -515,87 +443,6 @@ private void verifyInformantRegisterDocumentRequestHandlerResults() throws Event ); } - private JsonObject queryResponseWith(final String... payloads) { - final JsonArrayBuilder arrayBuilder = createArrayBuilder(); - for (final String payload : payloads) { - arrayBuilder.add(createObjectBuilder() - .add("prosecutionAuthorityId", PROSECUTION_AUTHORITY_ID.toString()) - .add("registerDate", REGISTER_DATE.toString()) - .add("payload", payload) - .build()); - } - return createObjectBuilder().add("informantRegisterDocumentRequests", arrayBuilder.build()).build(); - } - - private String buildV1PayloadJson() { - final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterOffence offence = - uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterOffence.informantRegisterOffence() - .withOffenceCode("offenceCode_V1") - .withOffenceTitle("offenceTitle_V1") - .withOrderIndex(1) - .withVerdictCode("GUILTY") - .build(); - final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterCaseOrApplication caseOrApplication = - uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication() - .withCaseOrApplicationReference("caseURN_V1") - .withOffences(List.of(offence)) - .build(); - final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDefendant defendant = - uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDefendant.informantRegisterDefendant() - .withFirstName("firstName_V1") - .withProsecutionCasesOrApplications(List.of(caseOrApplication)) - .build(); - final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearing hearing = - uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearing.informantRegisterHearing() - .withCourtRoom("courtroom_V1") - .withDefendants(List.of(defendant)) - .build(); - final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearingVenue hearingVenue = - uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearingVenue.informantRegisterHearingVenue() - .withCourtHouse("courtHouse_V1") - .withCourtSessions(List.of(hearing)) - .build(); - final uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest request = - uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest() - .withProsecutionAuthorityId(PROSECUTION_AUTHORITY_ID) - .withRegisterDate(REGISTER_DATE) - .withHearingVenue(hearingVenue) - .build(); - return objectToJsonObjectConverter.convert(request).toString(); - } - - private String buildV2PayloadJson() { - final InformantRegisterOffence offence = InformantRegisterOffence.informantRegisterOffence() - .withOffenceCode("offenceCode_V2") - .withOffenceTitle("offenceTitle_V2") - .withOrderIndex(1) - .withVerdict(Verdict.verdict().withVerdictCode("GUILTY").build()) - .build(); - final InformantRegisterCaseOrApplication caseOrApplication = - InformantRegisterCaseOrApplication.informantRegisterCaseOrApplication() - .withCaseOrApplicationReference("caseURN_V2") - .withOffences(List.of(offence)) - .build(); - final InformantRegisterDefendant defendant = InformantRegisterDefendant.informantRegisterDefendant() - .withFirstName("firstName_V2") - .withProsecutionCasesOrApplications(List.of(caseOrApplication)) - .build(); - final InformantRegisterHearing hearing = InformantRegisterHearing.informantRegisterHearing() - .withCourtRoom("courtroom_V2") - .withDefendants(List.of(defendant)) - .build(); - final InformantRegisterHearingVenue hearingVenue = InformantRegisterHearingVenue.informantRegisterHearingVenue() - .withCourtHouse("courtHouse_V2") - .withCourtSessions(List.of(hearing)) - .build(); - final InformantRegisterDocumentRequest request = informantRegisterDocumentRequest() - .withProsecutionAuthorityId(PROSECUTION_AUTHORITY_ID) - .withRegisterDate(REGISTER_DATE) - .withHearingVenue(hearingVenue) - .build(); - return objectToJsonObjectConverter.convert(request).toString(); - } - private Envelope buildEnvelope() { return buildEnvelope(null, false, false); } diff --git a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java index 54e207636..d767ffb1d 100644 --- a/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java +++ b/results-integration-test/src/test/java/uk/gov/moj/cpp/results/it/InformantRegisterEventFlowsIT.java @@ -4,19 +4,12 @@ import static java.time.ZonedDateTime.now; import static java.util.UUID.randomUUID; import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric; -import static org.apache.http.HttpStatus.SC_ACCEPTED; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static uk.gov.justice.services.messaging.JsonEnvelope.metadataBuilder; import static uk.gov.justice.services.messaging.JsonObjects.createArrayBuilder; import static uk.gov.justice.services.messaging.JsonObjects.createObjectBuilder; import static uk.gov.justice.services.messaging.JsonObjects.createReader; -import static uk.gov.moj.cpp.results.it.helper.InformantRegisterDocumentRequestHelper.getWriteUrl; -import static uk.gov.moj.cpp.results.it.helper.RestHelper.postCommand; import static uk.gov.moj.cpp.results.it.utils.FileUtil.getPayload; import static uk.gov.moj.cpp.results.it.utils.QueueUtil.privateEvents; -import static uk.gov.moj.cpp.results.it.utils.QueueUtil.retrieveMessage; import static uk.gov.moj.cpp.results.it.utils.QueueUtil.sendMessage; import static uk.gov.moj.cpp.results.it.utils.WireMockStubUtils.setupUsersGroupQueryStub; @@ -25,18 +18,13 @@ import java.io.StringReader; import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.List; import java.util.Random; import java.util.UUID; import javax.jms.JMSException; -import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.json.JsonObject; -import io.restassured.path.json.JsonPath; -import io.restassured.response.Response; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -53,14 +41,10 @@ *

  • {@code results.event.informant-register-generated-v2} (V2) advances a RECORDED register past RECORDED
  • * * - *

    The {@code recorded} flows are produced here synthetically (published straight onto the topic) to confirm the - * listener handlers resolve and project correctly. Since CIMD-3915 the command path emits the V1 generated event too - * (not only V2): {@code generate-informant-register} splits the queried registers by version and emits - * {@code informant-register-generated} for legacy (flat {@code verdictCode}) payloads and - * {@code informant-register-generated-v2} for structured-verdict payloads. The - * {@link #generateCommandWithMixedV1AndV2RecordedRegistersEmitsBothGeneratedEvents()} test exercises that split - * end-to-end through the real command. The {@code generated} events only transition existing RECORDED registers, so - * each generate test first injects the matching {@code recorded} event.

    + *

    The current command path only ever emits the V2 events, so the V1 events are produced here synthetically to + * confirm their listener handlers still resolve and project correctly (this is the listener-resolution guarantee the + * V2/generated-class consolidation depends on). The {@code generated} events only transition existing RECORDED + * registers, so each generate test first injects the matching {@code recorded} event.

    */ public class InformantRegisterEventFlowsIT { @@ -72,12 +56,6 @@ public class InformantRegisterEventFlowsIT { private static final String V2_DOCUMENT_REQUEST_RESOURCE = "json/informant-register/results.add-informant-register-document-request.json"; - private static final String GENERATE_COMMAND_MEDIA_TYPE = "application/vnd.results.generate-informant-register+json"; - - // Per-poll timeout while draining the topic of generated events after the command. Long enough to cover the - // async command -> event-store -> topic hop, short enough that the drain loop terminates once the topic is quiet. - private static final long DRAIN_TIMEOUT_MILLIS = 10000; - private static final Random RANDOM = new Random(); private InformantRegisterDocumentRequestHelper helper; @@ -147,62 +125,6 @@ public void generatedEventShouldAdvanceRecordedRegister() { helper.verifyInformantRegisterNoLongerRecorded(prosecutionAuthorityId); } - /** - * End-to-end exercise of the CIMD-3915 version split: a single prosecution authority + register date holding both - * a legacy V1 (flat {@code verdictCode}) and a new V2 (structured {@code verdict}) RECORDED register. The real - * {@code generate-informant-register} command groups them onto one stream and must emit BOTH a V1 - * {@code informant-register-generated} (preserving {@code verdictCode}) and a V2 {@code informant-register-generated-v2}. - * Before the fix the command emitted only the V2 event for every register, dropping the V1 {@code verdictCode}. - */ - @Test - public void generateCommandWithMixedV1AndV2RecordedRegistersEmitsBothGeneratedEvents() { - final UUID prosecutionAuthorityId = randomUUID(); - final ZonedDateTime registerDate = now(UTC); - - // Consumers must be live before the command is issued so the emitted generated events are captured. - final MessageConsumer v1GeneratedConsumer = privateEvents.createConsumer(EVENT_GENERATED); - final MessageConsumer v2GeneratedConsumer = privateEvents.createConsumer(EVENT_GENERATED_V2); - - final JsonObject v1Doc = v1DocumentRequest(prosecutionAuthorityId, registerDate, randomUUID()); - final JsonObject v2Doc = v2DocumentRequest(prosecutionAuthorityId, registerDate, randomUUID()); - publishRecorded(EVENT_RECORDED, prosecutionAuthorityId, v1Doc); - publishRecorded(EVENT_RECORDED_V2, prosecutionAuthorityId, v2Doc); - helper.verifyInformantRegisterRequestsExists(prosecutionAuthorityId); - - final Response response = postCommand(getWriteUrl("/informant-register/generate"), GENERATE_COMMAND_MEDIA_TYPE, ""); - assertThat(response.getStatusCode(), equalTo(SC_ACCEPTED)); - - final List v1Generated = drainGeneratedEventsFor(v1GeneratedConsumer, prosecutionAuthorityId); - final List v2Generated = drainGeneratedEventsFor(v2GeneratedConsumer, prosecutionAuthorityId); - - assertThat("expected a V1 informant-register-generated event for the V1-recorded register", - v1Generated.size(), greaterThanOrEqualTo(1)); - assertThat("V1 generated event must preserve the legacy flat verdictCode", - v1Generated.get(0).getString("informantRegisterDocumentRequests[0].hearingVenue.courtSessions[0]" - + ".defendants[0].prosecutionCasesOrApplications[0].offences[0].verdictCode"), - equalTo("G")); - assertThat("expected a V2 informant-register-generated-v2 event for the V2-recorded register", - v2Generated.size(), greaterThanOrEqualTo(1)); - } - - /** - * Drains all currently-available generated events from the consumer (the unscoped generate command advances every - * RECORDED register in the view-store) and keeps only those belonging to the given prosecution authority, so - * registers seeded by other tests do not cause false positives or negatives. - */ - private List drainGeneratedEventsFor(final MessageConsumer consumer, final UUID prosecutionAuthorityId) { - final List matches = new ArrayList<>(); - JsonPath message = retrieveMessage(consumer, DRAIN_TIMEOUT_MILLIS); - while (message != null) { - final String paId = message.getString("informantRegisterDocumentRequests[0].prosecutionAuthorityId"); - if (prosecutionAuthorityId.toString().equals(paId)) { - matches.add(message); - } - message = retrieveMessage(consumer, DRAIN_TIMEOUT_MILLIS); - } - return matches; - } - private void publishRecorded(final String eventName, final UUID prosecutionAuthorityId, final JsonObject documentRequest) { final JsonObject payload = createObjectBuilder() .add("prosecutionAuthorityId", prosecutionAuthorityId.toString()) From 6908d4d879bcb1048dd0e8f40821c251536a89da Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Thu, 18 Jun 2026 16:27:03 +0100 Subject: [PATCH 11/22] CIMD-3915 : verdict implementation --- ...rmantRegisterDocumentRequestQueryView.java | 79 ++++++++++++++- ...tRegisterDocumentRequestQueryViewTest.java | 99 +++++++++++++++++++ 2 files changed, 176 insertions(+), 2 deletions(-) diff --git a/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java b/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java index af0d0145a..799a25eb8 100644 --- a/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java +++ b/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java @@ -10,6 +10,7 @@ import static uk.gov.justice.services.messaging.JsonObjects.getString; import uk.gov.justice.services.common.converter.ObjectToJsonObjectConverter; +import uk.gov.justice.services.common.converter.StringToJsonObjectConverter; import uk.gov.justice.services.core.annotation.Component; import uk.gov.justice.services.core.annotation.Handles; import uk.gov.justice.services.core.annotation.ServiceComponent; @@ -26,9 +27,11 @@ import java.util.stream.Collectors; import javax.inject.Inject; +import javax.json.JsonArray; import javax.json.JsonArrayBuilder; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; +import javax.json.JsonValue; import org.apache.commons.lang3.StringUtils; @@ -39,6 +42,11 @@ public class InformantRegisterDocumentRequestQueryView { private static final String FIELD_FILE_ID = "fileId"; private static final String FIELD_PROSECUTION_AUTHORITY_CODE = "prosecutionAuthorityCode"; private static final String FIELD_REGISTER_DATE = "registerDate"; + private static final String FIELD_PAYLOAD = "payload"; + private static final String FIELD_VERDICT = "verdict"; + private static final String FIELD_VERDICT_CODE = "verdictCode"; + private static final String FIELD_VERDICT_TYPE = "verdictType"; + private static final String FIELD_VERDICT_DATE = "verdictDate"; @Inject private InformantRegisterRepository informantRegisterRepository; @@ -46,6 +54,9 @@ public class InformantRegisterDocumentRequestQueryView { @Inject private ObjectToJsonObjectConverter objectToJsonObjectConverter; + @Inject + private StringToJsonObjectConverter stringToJsonObjectConverter; + @Handles("results.query.informant-register-document-request") public JsonEnvelope getInformantRegisterRequests(final JsonEnvelope envelope) { final JsonObjectBuilder jsonObjectBuilder = createObjectBuilder(); @@ -54,16 +65,80 @@ public JsonEnvelope getInformantRegisterRequests(final JsonEnvelope envelope) { if (isNotBlank(requestStatus)) { if(RegisterStatus.RECORDED.toString().equalsIgnoreCase(requestStatus)) { final List informantRegisterEntities = informantRegisterRepository.findByStatusRecorded(); - informantRegisterEntities.forEach(informantRegisterEntity -> jsonArrayBuilder.add(objectToJsonObjectConverter.convert(informantRegisterEntity))); + informantRegisterEntities.forEach(informantRegisterEntity -> jsonArrayBuilder.add(convertWithNormalisedVerdict(informantRegisterEntity))); } else { final List informantRegisterEntities = informantRegisterRepository.findByStatus(RegisterStatus.valueOf(requestStatus)); - informantRegisterEntities.forEach(informantRegisterEntity -> jsonArrayBuilder.add(objectToJsonObjectConverter.convert(informantRegisterEntity))); + informantRegisterEntities.forEach(informantRegisterEntity -> jsonArrayBuilder.add(convertWithNormalisedVerdict(informantRegisterEntity))); } } return envelopeFrom(envelope.metadata(), jsonObjectBuilder.add(FIELD_INFORMANT_REGISTER_DOCUMENTS, jsonArrayBuilder.build()).build()); } + /** + * Converts the entity to JSON and normalises every offence in the stored payload so that the + * verdict is always represented as a structured {@code verdict} object. Legacy payloads that + * carry only a bare {@code verdictCode} string on the offence are rewritten to + * {@code {"verdictCode": , "verdictType": null, "verdictDate": null}}; payloads that + * already hold a {@code verdict} object are left untouched. + */ + private JsonObject convertWithNormalisedVerdict(final InformantRegisterEntity informantRegisterEntity) { + final JsonObject converted = objectToJsonObjectConverter.convert(informantRegisterEntity); + final String payload = converted.getString(FIELD_PAYLOAD, null); + if (StringUtils.isBlank(payload)) { + return converted; + } + final String normalisedPayload = deepTransformObject(stringToJsonObjectConverter.convert(payload)).toString(); + final JsonObjectBuilder builder = createObjectBuilder(); + converted.forEach((key, value) -> { + if (FIELD_PAYLOAD.equals(key)) { + builder.add(FIELD_PAYLOAD, normalisedPayload); + } else { + builder.add(key, value); + } + }); + return builder.build(); + } + + private JsonObject deepTransformObject(final JsonObject object) { + final boolean wrapVerdictCode = object.containsKey(FIELD_VERDICT_CODE) && !object.containsKey(FIELD_VERDICT); + final JsonObjectBuilder builder = createObjectBuilder(); + object.forEach((key, value) -> { + if (wrapVerdictCode && FIELD_VERDICT_CODE.equals(key)) { + return; + } + if (!FIELD_VERDICT.equals(key) && value.getValueType() == JsonValue.ValueType.OBJECT) { + builder.add(key, deepTransformObject(value.asJsonObject())); + } else if (value.getValueType() == JsonValue.ValueType.ARRAY) { + builder.add(key, deepTransformArray(value.asJsonArray())); + } else { + builder.add(key, value); + } + }); + if (wrapVerdictCode) { + builder.add(FIELD_VERDICT, createObjectBuilder() + .add(FIELD_VERDICT_CODE, object.getString(FIELD_VERDICT_CODE)) + .addNull(FIELD_VERDICT_TYPE) + .addNull(FIELD_VERDICT_DATE) + .build()); + } + return builder.build(); + } + + private JsonArray deepTransformArray(final JsonArray array) { + final JsonArrayBuilder builder = createArrayBuilder(); + array.forEach(item -> { + if (item.getValueType() == JsonValue.ValueType.OBJECT) { + builder.add(deepTransformObject(item.asJsonObject())); + } else if (item.getValueType() == JsonValue.ValueType.ARRAY) { + builder.add(deepTransformArray(item.asJsonArray())); + } else { + builder.add(item); + } + }); + return builder.build(); + } + @Handles("results.query.informant-register-document-by-material") public JsonEnvelope getInformantRegistersByMaterial(final JsonEnvelope envelope) { final UUID fileId = fromString(envelope.payloadAsJsonObject().getString(FIELD_FILE_ID)); diff --git a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java index 4c7bf3a72..8bfd0a5c1 100644 --- a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java +++ b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java @@ -3,6 +3,7 @@ import static com.google.common.collect.Lists.newArrayList; import static java.time.LocalDate.now; import static java.util.UUID.randomUUID; +import static uk.gov.justice.services.messaging.JsonObjects.createArrayBuilder; import static uk.gov.justice.services.messaging.JsonObjects.createObjectBuilder; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; @@ -12,6 +13,7 @@ import static uk.gov.moj.cpp.domains.constant.RegisterStatus.RECORDED; import uk.gov.justice.services.common.converter.ObjectToJsonObjectConverter; +import uk.gov.justice.services.common.converter.StringToJsonObjectConverter; import uk.gov.justice.services.messaging.JsonEnvelope; import uk.gov.moj.cpp.results.persist.InformantRegisterRepository; import uk.gov.moj.cpp.results.persist.entity.InformantRegisterEntity; @@ -26,6 +28,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) @@ -37,6 +40,9 @@ public class InformantRegisterDocumentRequestQueryViewTest { @Mock private ObjectToJsonObjectConverter objectToJsonObjectConverter; + @Spy + private StringToJsonObjectConverter stringToJsonObjectConverter = new StringToJsonObjectConverter(); + @InjectMocks private InformantRegisterDocumentRequestQueryView informantRegisterDocumentRequestQueryView; @@ -154,4 +160,97 @@ public void shouldGetInformantRegisterByDateWhenProsecutionAuthorityIsEmpty() { assertThat(informantRegisterRequests.payloadAsJsonObject().getJsonArray("informantRegisterDocumentRequests") .getJsonObject(0).getString("registerDate"), is(registerDate.toString())); } + + @Test + public void shouldConvertOffenceVerdictCodeToVerdictObjectWhenOnlyVerdictCodeIsPresent() { + final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); + final JsonObject converted = createObjectBuilder() + .add("payload", payloadWithOffence(createObjectBuilder().add("verdictCode", "G"))) + .build(); + + final JsonEnvelope envelope = recordedRequestEnvelope(); + when(objectToJsonObjectConverter.convert(informantRegisterEntity)).thenReturn(converted); + when(informantRegisterRepository.findByStatusRecorded()).thenReturn(Collections.singletonList(informantRegisterEntity)); + + final JsonObject offence = firstOffence(informantRegisterDocumentRequestQueryView.getInformantRegisterRequests(envelope)); + + assertThat(offence.containsKey("verdictCode"), is(false)); + assertThat(offence.getJsonObject("verdict").getString("verdictCode"), is("G")); + assertThat(offence.getJsonObject("verdict").isNull("verdictType"), is(true)); + assertThat(offence.getJsonObject("verdict").isNull("verdictDate"), is(true)); + } + + @Test + public void shouldKeepVerdictObjectAsIsWhenAlreadyPresent() { + final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); + final JsonObject verdictObject = createObjectBuilder() + .add("verdictCode", "G") + .add("verdictType", "FOUND_GUILTY") + .add("verdictDate", "2026-04-13") + .build(); + final JsonObject converted = createObjectBuilder() + .add("payload", payloadWithOffence(createObjectBuilder().add("verdict", verdictObject))) + .build(); + + final JsonEnvelope envelope = recordedRequestEnvelope(); + when(objectToJsonObjectConverter.convert(informantRegisterEntity)).thenReturn(converted); + when(informantRegisterRepository.findByStatusRecorded()).thenReturn(Collections.singletonList(informantRegisterEntity)); + + final JsonObject offence = firstOffence(informantRegisterDocumentRequestQueryView.getInformantRegisterRequests(envelope)); + + assertThat(offence.containsKey("verdictCode"), is(false)); + assertThat(offence.getJsonObject("verdict").getString("verdictCode"), is("G")); + assertThat(offence.getJsonObject("verdict").getString("verdictType"), is("FOUND_GUILTY")); + assertThat(offence.getJsonObject("verdict").getString("verdictDate"), is("2026-04-13")); + } + + private JsonEnvelope recordedRequestEnvelope() { + return envelopeFrom(metadataBuilder().withId(randomUUID()) + .withName("results.query.informant-register-document-request").build(), + createObjectBuilder().add("requestStatus", RECORDED.name()).build()); + } + + private String payloadWithOffence(final javax.json.JsonObjectBuilder offenceBuilder) { + final JsonObject offence = offenceBuilder + .add("offenceCode", "PS90010") + .add("orderIndex", 1) + .add("offenceTitle", "An offence") + .build(); + final JsonObject caseOrApplication = createObjectBuilder() + .add("caseOrApplicationReference", "TFL4359536") + .add("offences", createArrayBuilder().add(offence).build()) + .build(); + final JsonObject defendant = createObjectBuilder() + .add("name", "Fred Smith") + .add("address1", "Flat 1") + .add("prosecutionCasesOrApplications", createArrayBuilder().add(caseOrApplication).build()) + .build(); + final JsonObject hearing = createObjectBuilder() + .add("courtRoom", "Room name") + .add("hearingStartTime", "2020-03-12") + .add("defendants", createArrayBuilder().add(defendant).build()) + .build(); + final JsonObject hearingVenue = createObjectBuilder() + .add("courtHouse", "Lavender Hill Magistrates' Court") + .add("courtSessions", createArrayBuilder().add(hearing).build()) + .build(); + return createObjectBuilder() + .add("fileName", "InformantRegister_TFL_2020-04-12.csv") + .add("hearingVenue", hearingVenue) + .build() + .toString(); + } + + private JsonObject firstOffence(final JsonEnvelope informantRegisterRequests) { + final String payload = informantRegisterRequests.payloadAsJsonObject() + .getJsonArray("informantRegisterDocumentRequests") + .getJsonObject(0) + .getString("payload"); + return stringToJsonObjectConverter.convert(payload) + .getJsonObject("hearingVenue") + .getJsonArray("courtSessions").getJsonObject(0) + .getJsonArray("defendants").getJsonObject(0) + .getJsonArray("prosecutionCasesOrApplications").getJsonObject(0) + .getJsonArray("offences").getJsonObject(0); + } } From cb0d91a133d2846fb3cf31b657081c174e030ba6 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Fri, 19 Jun 2026 10:42:14 +0100 Subject: [PATCH 12/22] CIMD-3915 : verdict implementation --- .../event/InformantRegisterEventListener.java | 28 +++++++--- .../InformantRegisterEventListenerTest.java | 53 +++++++++++++++++++ 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java index 5b2584e21..fa1a3e16c 100644 --- a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java +++ b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java @@ -30,10 +30,14 @@ import javax.transaction.Transactional; import org.apache.commons.lang3.BooleanUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @ServiceComponent(EVENT_LISTENER) public class InformantRegisterEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(InformantRegisterEventListener.class); + private static final String INFORMANT_REGISTER_REQUEST_PARAM = "informantRegister"; @Inject @@ -89,8 +93,14 @@ public void generateInformantRegister(final JsonEnvelope event) { final InformantRegisterGenerated informantRegisterGenerated = jsonObjectToObjectConverter.convert(payload, InformantRegisterGenerated.class); final ZonedDateTime currentDateTime = now(); - final UUID prosecutionAuthorityId = informantRegisterGenerated.getInformantRegisterDocumentRequests().get(0).getProsecutionAuthorityId(); - final LocalDate registerDate = informantRegisterGenerated.getInformantRegisterDocumentRequests().get(0).getRegisterDate().toLocalDate(); + final List documentRequests = informantRegisterGenerated.getInformantRegisterDocumentRequests(); + if (documentRequests == null || documentRequests.isEmpty()) { + LOGGER.warn("Skipping results.event.informant-register-generated: no informantRegisterDocumentRequests present"); + return; + } + + final UUID prosecutionAuthorityId = documentRequests.get(0).getProsecutionAuthorityId(); + final LocalDate registerDate = documentRequests.get(0).getRegisterDate().toLocalDate(); final List informantRegisters = informantRegisterRepository.findByProsecutionAuthorityIdAndRegisterDateForStatusRecorded(prosecutionAuthorityId, registerDate); informantRegisters.forEach(informantRegisterEntity -> { @@ -102,7 +112,7 @@ public void generateInformantRegister(final JsonEnvelope event) { } }); - informantRegisterGenerated.getInformantRegisterDocumentRequests().stream().map(InformantRegisterDocumentRequest::getHearingId).forEach(hearingId -> { + documentRequests.stream().map(InformantRegisterDocumentRequest::getHearingId).forEach(hearingId -> { final List informantRegistersList = informantRegisterRepository.findByHearingIdAndStatusRecorded(hearingId); informantRegistersList.forEach(informantRegisterEntity -> informantRegisterEntity.setProcessedOn(currentDateTime)); }); @@ -115,8 +125,14 @@ public void generateInformantRegisterV2(final JsonEnvelope event) { final InformantRegisterGeneratedV2 informantRegisterGenerated = jsonObjectToObjectConverter.convert(payload, InformantRegisterGeneratedV2.class); final ZonedDateTime currentDateTime = now(); - final UUID prosecutionAuthorityId = informantRegisterGenerated.getInformantRegisterDocumentRequests().get(0).getProsecutionAuthorityId(); - final LocalDate registerDate = informantRegisterGenerated.getInformantRegisterDocumentRequests().get(0).getRegisterDate().toLocalDate(); + final List documentRequests = informantRegisterGenerated.getInformantRegisterDocumentRequests(); + if (documentRequests == null || documentRequests.isEmpty()) { + LOGGER.warn("Skipping results.event.informant-register-generated-v2: no informantRegisterDocumentRequests present"); + return; + } + + final UUID prosecutionAuthorityId = documentRequests.get(0).getProsecutionAuthorityId(); + final LocalDate registerDate = documentRequests.get(0).getRegisterDate().toLocalDate(); final List informantRegisters = informantRegisterRepository.findByProsecutionAuthorityIdAndRegisterDateForStatusRecorded(prosecutionAuthorityId, registerDate); informantRegisters.forEach(informantRegisterEntity -> { @@ -128,7 +144,7 @@ public void generateInformantRegisterV2(final JsonEnvelope event) { } }); - informantRegisterGenerated.getInformantRegisterDocumentRequests().stream() + documentRequests.stream() .map(uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest::getHearingId) .forEach(hearingId -> { final List informantRegistersList = informantRegisterRepository.findByHearingIdAndStatusRecorded(hearingId); diff --git a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java index 68abc86dd..cd7e4a88a 100644 --- a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java +++ b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java @@ -8,6 +8,7 @@ import static org.hamcrest.core.Is.is; import static org.mockito.ArgumentCaptor.forClass; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import static uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterDocumentRequest.informantRegisterDocumentRequest; import static uk.gov.justice.core.courts.informantRegisterDocument.InformantRegisterHearingVenue.informantRegisterHearingVenue; @@ -301,6 +302,58 @@ public void generateInformantRegister_withPreMigrationFlatVerdictCode_shouldNotT assertThat(informantRegisterEntity.getStatus(), is(GENERATED)); } + @Test + public void generateInformantRegister_withNullDocumentRequests_shouldSkipAndNotInteractWithRepository() { + final JsonObject payload = createObjectBuilder() + .add("fileId", randomUUID().toString()) + .add("systemGenerated", false) + .build(); + + informantRegisterEventListener.generateInformantRegister(envelopeFrom( + metadataWithRandomUUID("results.event.informant-register-generated"), payload)); + + verifyNoInteractions(informantRegisterRepository); + } + + @Test + public void generateInformantRegister_withEmptyDocumentRequests_shouldSkipAndNotInteractWithRepository() { + final JsonObject payload = createObjectBuilder() + .add("informantRegisterDocumentRequests", createArrayBuilder()) + .add("fileId", randomUUID().toString()) + .add("systemGenerated", false) + .build(); + + informantRegisterEventListener.generateInformantRegister(envelopeFrom( + metadataWithRandomUUID("results.event.informant-register-generated"), payload)); + + verifyNoInteractions(informantRegisterRepository); + } + + @Test + public void generateInformantRegisterV2_withNullDocumentRequests_shouldSkipAndNotInteractWithRepository() { + final JsonObject payload = createObjectBuilder() + .add("systemGenerated", false) + .build(); + + informantRegisterEventListener.generateInformantRegisterV2(envelopeFrom( + metadataWithRandomUUID("results.event.informant-register-generated-v2"), payload)); + + verifyNoInteractions(informantRegisterRepository); + } + + @Test + public void generateInformantRegisterV2_withEmptyDocumentRequests_shouldSkipAndNotInteractWithRepository() { + final JsonObject payload = createObjectBuilder() + .add("informantRegisterDocumentRequests", createArrayBuilder()) + .add("systemGenerated", false) + .build(); + + informantRegisterEventListener.generateInformantRegisterV2(envelopeFrom( + metadataWithRandomUUID("results.event.informant-register-generated-v2"), payload)); + + verifyNoInteractions(informantRegisterRepository); + } + @Test public void shouldNotifyInformantRegister() { final UUID prosecutionAuthId = randomUUID(); From b290aaf3eefc7785e8623321013c522b3b736b59 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Fri, 19 Jun 2026 10:58:08 +0100 Subject: [PATCH 13/22] CIMD-3915 : verdict implementation --- .../event/InformantRegisterEventListener.java | 41 ++++++++++--------- ...rmantRegisterDocumentRequestQueryView.java | 2 +- ...tRegisterDocumentRequestQueryViewTest.java | 23 +++++++++++ 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java index fa1a3e16c..2d3ccb7a2 100644 --- a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java +++ b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java @@ -49,22 +49,12 @@ public class InformantRegisterEventListener { @Transactional @Handles("results.event.informant-register-recorded") public void saveInformantRegister(final JsonEnvelope event) { - final JsonObject informantRegisterDocumentRequestJson = event.payloadAsJsonObject().getJsonObject(INFORMANT_REGISTER_REQUEST_PARAM); + final InformantRegisterDocumentRequest documentRequest = jsonObjectToObjectConverter.convert(informantRegisterDocumentRequestJson, InformantRegisterDocumentRequest.class); - final InformantRegisterDocumentRequest informantRegisterDocumentRequest = jsonObjectToObjectConverter.convert(informantRegisterDocumentRequestJson, InformantRegisterDocumentRequest.class); - - final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); - informantRegisterEntity.setId(randomUUID()); - informantRegisterEntity.setRegisterDate(informantRegisterDocumentRequest.getRegisterDate().toLocalDate()); - informantRegisterEntity.setRegisterTime(informantRegisterDocumentRequest.getRegisterDate()); - informantRegisterEntity.setHearingId(informantRegisterDocumentRequest.getHearingId()); - informantRegisterEntity.setProsecutionAuthorityId(informantRegisterDocumentRequest.getProsecutionAuthorityId()); - informantRegisterEntity.setProsecutionAuthorityCode(informantRegisterDocumentRequest.getProsecutionAuthorityCode()); - informantRegisterEntity.setProsecutionAuthorityOuCode(informantRegisterDocumentRequest.getProsecutionAuthorityOuCode()); - informantRegisterEntity.setPayload(informantRegisterDocumentRequestJson.toString()); - informantRegisterEntity.setStatus(RECORDED); - informantRegisterRepository.save(informantRegisterEntity); + saveRecordedInformantRegister(informantRegisterDocumentRequestJson, documentRequest.getRegisterDate(), + documentRequest.getHearingId(), documentRequest.getProsecutionAuthorityId(), + documentRequest.getProsecutionAuthorityCode(), documentRequest.getProsecutionAuthorityOuCode()); } @Transactional @@ -74,14 +64,25 @@ public void saveInformantRegisterV2(final JsonEnvelope event) { final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest documentRequest = jsonObjectToObjectConverter.convert(informantRegisterDocumentRequestJson, uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest.class); + saveRecordedInformantRegister(informantRegisterDocumentRequestJson, documentRequest.getRegisterDate(), + documentRequest.getHearingId(), documentRequest.getProsecutionAuthorityId(), + documentRequest.getProsecutionAuthorityCode(), documentRequest.getProsecutionAuthorityOuCode()); + } + + private void saveRecordedInformantRegister(final JsonObject informantRegisterDocumentRequestJson, + final ZonedDateTime registerDate, + final UUID hearingId, + final UUID prosecutionAuthorityId, + final String prosecutionAuthorityCode, + final String prosecutionAuthorityOuCode) { final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); informantRegisterEntity.setId(randomUUID()); - informantRegisterEntity.setRegisterDate(documentRequest.getRegisterDate().toLocalDate()); - informantRegisterEntity.setRegisterTime(documentRequest.getRegisterDate()); - informantRegisterEntity.setHearingId(documentRequest.getHearingId()); - informantRegisterEntity.setProsecutionAuthorityId(documentRequest.getProsecutionAuthorityId()); - informantRegisterEntity.setProsecutionAuthorityCode(documentRequest.getProsecutionAuthorityCode()); - informantRegisterEntity.setProsecutionAuthorityOuCode(documentRequest.getProsecutionAuthorityOuCode()); + informantRegisterEntity.setRegisterDate(registerDate.toLocalDate()); + informantRegisterEntity.setRegisterTime(registerDate); + informantRegisterEntity.setHearingId(hearingId); + informantRegisterEntity.setProsecutionAuthorityId(prosecutionAuthorityId); + informantRegisterEntity.setProsecutionAuthorityCode(prosecutionAuthorityCode); + informantRegisterEntity.setProsecutionAuthorityOuCode(prosecutionAuthorityOuCode); informantRegisterEntity.setPayload(informantRegisterDocumentRequestJson.toString()); informantRegisterEntity.setStatus(RECORDED); informantRegisterRepository.save(informantRegisterEntity); diff --git a/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java b/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java index 799a25eb8..1c88e3c0e 100644 --- a/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java +++ b/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java @@ -177,7 +177,7 @@ public JsonEnvelope getInformantRegistersByRequestDate(final JsonEnvelope envelo .flatMap(List::stream) .collect(Collectors.toList()); } - informantRegisterEntities.forEach(i -> jsonArrayBuilder.add(objectToJsonObjectConverter.convert(i))); + informantRegisterEntities.forEach(i -> jsonArrayBuilder.add(convertWithNormalisedVerdict(i))); }); return envelopeFrom(envelope.metadata(), diff --git a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java index 8bfd0a5c1..1949b2c6b 100644 --- a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java +++ b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java @@ -204,6 +204,29 @@ public void shouldKeepVerdictObjectAsIsWhenAlreadyPresent() { assertThat(offence.getJsonObject("verdict").getString("verdictDate"), is("2026-04-13")); } + @Test + public void shouldConvertOffenceVerdictCodeToVerdictObjectForByDateQueryWhenOnlyVerdictCodeIsPresent() { + final LocalDate registerDate = now(); + final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); + final JsonObject converted = createObjectBuilder() + .add("payload", payloadWithOffence(createObjectBuilder().add("verdictCode", "G"))) + .build(); + + final JsonObject payload = createObjectBuilder().add("registerDate", registerDate.toString()).build(); + final JsonEnvelope envelope = envelopeFrom(metadataBuilder().withId(randomUUID()) + .withName("results.query.informant-register-document-by-request-date").build(), payload); + + when(objectToJsonObjectConverter.convert(informantRegisterEntity)).thenReturn(converted); + when(informantRegisterRepository.findByRegisterDate(registerDate)).thenReturn(newArrayList(informantRegisterEntity)); + + final JsonObject offence = firstOffence(informantRegisterDocumentRequestQueryView.getInformantRegistersByRequestDate(envelope)); + + assertThat(offence.containsKey("verdictCode"), is(false)); + assertThat(offence.getJsonObject("verdict").getString("verdictCode"), is("G")); + assertThat(offence.getJsonObject("verdict").isNull("verdictType"), is(true)); + assertThat(offence.getJsonObject("verdict").isNull("verdictDate"), is(true)); + } + private JsonEnvelope recordedRequestEnvelope() { return envelopeFrom(metadataBuilder().withId(randomUUID()) .withName("results.query.informant-register-document-request").build(), From ca2bec0dcca2aa750f5df6274c3f79b3df7f2e59 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Fri, 19 Jun 2026 11:01:06 +0100 Subject: [PATCH 14/22] CIMD-3915 : verdict implementation --- .../InformantRegisterEventListenerTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java index cd7e4a88a..476f5da70 100644 --- a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java +++ b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java @@ -107,7 +107,7 @@ public void shouldSaveInformantRegisterRequested() { } @Test - public void saveInformantRegisterV2_shouldSaveEntityFromLocalDocumentRequest() { + void saveInformantRegisterV2_shouldSaveEntityFromLocalDocumentRequest() { final UUID prosecutionAuthId = randomUUID(); final String ouCode = randomAlphanumeric(10); final ZonedDateTime registerDate = ZonedDateTime.parse("2026-04-13T09:00:00Z"); @@ -171,7 +171,7 @@ public void shouldSaveInformantRegisterGenerated() { } @Test - public void generateInformantRegisterV2_shouldSetEntityStatusToGenerated() { + void generateInformantRegisterV2_shouldSetEntityStatusToGenerated() { final UUID prosecutionAuthId = randomUUID(); final ZonedDateTime registerDate = ZonedDateTime.parse("2026-04-13T09:00:00Z"); @@ -201,7 +201,7 @@ public void generateInformantRegisterV2_shouldSetEntityStatusToGenerated() { } @Test - public void saveInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow() { + void saveInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow() { final UUID prosecutionAuthId = randomUUID(); final JsonObject offenceJson = createObjectBuilder() .add("offenceCode", "PS90010") @@ -251,7 +251,7 @@ public void saveInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow } @Test - public void generateInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow() { + void generateInformantRegister_withPreMigrationFlatVerdictCode_shouldNotThrow() { final UUID prosecutionAuthId = randomUUID(); final LocalDate registerDate = LocalDate.parse("2026-04-13"); final JsonObject offenceJson = createObjectBuilder() @@ -303,7 +303,7 @@ public void generateInformantRegister_withPreMigrationFlatVerdictCode_shouldNotT } @Test - public void generateInformantRegister_withNullDocumentRequests_shouldSkipAndNotInteractWithRepository() { + void generateInformantRegister_withNullDocumentRequests_shouldSkipAndNotInteractWithRepository() { final JsonObject payload = createObjectBuilder() .add("fileId", randomUUID().toString()) .add("systemGenerated", false) @@ -316,7 +316,7 @@ public void generateInformantRegister_withNullDocumentRequests_shouldSkipAndNotI } @Test - public void generateInformantRegister_withEmptyDocumentRequests_shouldSkipAndNotInteractWithRepository() { + void generateInformantRegister_withEmptyDocumentRequests_shouldSkipAndNotInteractWithRepository() { final JsonObject payload = createObjectBuilder() .add("informantRegisterDocumentRequests", createArrayBuilder()) .add("fileId", randomUUID().toString()) @@ -330,7 +330,7 @@ public void generateInformantRegister_withEmptyDocumentRequests_shouldSkipAndNot } @Test - public void generateInformantRegisterV2_withNullDocumentRequests_shouldSkipAndNotInteractWithRepository() { + void generateInformantRegisterV2_withNullDocumentRequests_shouldSkipAndNotInteractWithRepository() { final JsonObject payload = createObjectBuilder() .add("systemGenerated", false) .build(); @@ -342,7 +342,7 @@ public void generateInformantRegisterV2_withNullDocumentRequests_shouldSkipAndNo } @Test - public void generateInformantRegisterV2_withEmptyDocumentRequests_shouldSkipAndNotInteractWithRepository() { + void generateInformantRegisterV2_withEmptyDocumentRequests_shouldSkipAndNotInteractWithRepository() { final JsonObject payload = createObjectBuilder() .add("informantRegisterDocumentRequests", createArrayBuilder()) .add("systemGenerated", false) From fe6cbb90e88bec10fbb2d8534dffb54254220a88 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Fri, 19 Jun 2026 11:04:16 +0100 Subject: [PATCH 15/22] CIMD-3915 : verdict implementation --- ...rmantRegisterDocumentRequestQueryView.java | 2 +- ...tRegisterDocumentRequestQueryViewTest.java | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java b/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java index 1c88e3c0e..93e0be714 100644 --- a/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java +++ b/results-query/results-query-view/src/main/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryView.java @@ -147,7 +147,7 @@ public JsonEnvelope getInformantRegistersByMaterial(final JsonEnvelope envelope) final JsonArrayBuilder jsonArrayBuilder = createArrayBuilder(); final List informantRegisterEntities = informantRegisterRepository.findByFileId(fileId); - informantRegisterEntities.forEach(informantRegisterEntity -> jsonArrayBuilder.add(objectToJsonObjectConverter.convert(informantRegisterEntity))); + informantRegisterEntities.forEach(informantRegisterEntity -> jsonArrayBuilder.add(convertWithNormalisedVerdict(informantRegisterEntity))); return envelopeFrom(envelope.metadata(), jsonObjectBuilder.add(FIELD_INFORMANT_REGISTER_DOCUMENTS, jsonArrayBuilder.build()).build()); diff --git a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java index 1949b2c6b..89301cb56 100644 --- a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java +++ b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java @@ -227,6 +227,29 @@ public void shouldConvertOffenceVerdictCodeToVerdictObjectForByDateQueryWhenOnly assertThat(offence.getJsonObject("verdict").isNull("verdictDate"), is(true)); } + @Test + public void shouldConvertOffenceVerdictCodeToVerdictObjectForByMaterialQueryWhenOnlyVerdictCodeIsPresent() { + final UUID fileId = randomUUID(); + final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); + final JsonObject converted = createObjectBuilder() + .add("payload", payloadWithOffence(createObjectBuilder().add("verdictCode", "G"))) + .build(); + + final JsonObject payload = createObjectBuilder().add("fileId", fileId.toString()).build(); + final JsonEnvelope envelope = envelopeFrom(metadataBuilder().withId(randomUUID()) + .withName("results.query.informant-register-document-by-material").build(), payload); + + when(objectToJsonObjectConverter.convert(informantRegisterEntity)).thenReturn(converted); + when(informantRegisterRepository.findByFileId(fileId)).thenReturn(newArrayList(informantRegisterEntity)); + + final JsonObject offence = firstOffence(informantRegisterDocumentRequestQueryView.getInformantRegistersByMaterial(envelope)); + + assertThat(offence.containsKey("verdictCode"), is(false)); + assertThat(offence.getJsonObject("verdict").getString("verdictCode"), is("G")); + assertThat(offence.getJsonObject("verdict").isNull("verdictType"), is(true)); + assertThat(offence.getJsonObject("verdict").isNull("verdictDate"), is(true)); + } + private JsonEnvelope recordedRequestEnvelope() { return envelopeFrom(metadataBuilder().withId(randomUUID()) .withName("results.query.informant-register-document-request").build(), From 62faaf9e504c71f43f067377348bc1cbf9ef2457 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Fri, 19 Jun 2026 11:10:13 +0100 Subject: [PATCH 16/22] CIMD-3915 : verdict implementation --- .../results/event/InformantRegisterEventListenerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java index 476f5da70..4d9ecf790 100644 --- a/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java +++ b/results-event/results-event-listener/src/test/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListenerTest.java @@ -2,7 +2,6 @@ import static java.util.Collections.singletonList; import static java.util.UUID.randomUUID; -import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.core.Is.is; @@ -45,6 +44,7 @@ import javax.json.JsonObject; import com.google.common.collect.Lists; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -79,7 +79,7 @@ public void setup() { @Test public void shouldSaveInformantRegisterRequested() { final UUID prosecutionAuthId = randomUUID(); - final String ouCode = randomAlphanumeric(10); + final String ouCode = RandomStringUtils.insecure().nextAlphanumeric(10); final InformantRegisterDocumentRequest informantRegisterDocumentRequest = informantRegisterDocumentRequest() .withProsecutionAuthorityId(prosecutionAuthId) .withProsecutionAuthorityOuCode(ouCode) @@ -109,7 +109,7 @@ public void shouldSaveInformantRegisterRequested() { @Test void saveInformantRegisterV2_shouldSaveEntityFromLocalDocumentRequest() { final UUID prosecutionAuthId = randomUUID(); - final String ouCode = randomAlphanumeric(10); + final String ouCode = RandomStringUtils.insecure().nextAlphanumeric(10); final ZonedDateTime registerDate = ZonedDateTime.parse("2026-04-13T09:00:00Z"); final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest localDocumentRequest = From 3300f43cabcb0aeb40a9e1ad669916d04c2168b7 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Fri, 19 Jun 2026 11:28:31 +0100 Subject: [PATCH 17/22] CIMD-3915 : sonar --- .../event/InformantRegisterEventListener.java | 12 ++++++------ ...nformantRegisterDocumentRequestQueryViewTest.java | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java index 2d3ccb7a2..355b200e0 100644 --- a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java +++ b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java @@ -60,13 +60,13 @@ public void saveInformantRegister(final JsonEnvelope event) { @Transactional @Handles("results.event.informant-register-recorded-v2") public void saveInformantRegisterV2(final JsonEnvelope event) { - final JsonObject informantRegisterDocumentRequestJson = event.payloadAsJsonObject().getJsonObject(INFORMANT_REGISTER_REQUEST_PARAM); - final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest documentRequest = - jsonObjectToObjectConverter.convert(informantRegisterDocumentRequestJson, uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest.class); + final JsonObject informantRegisterDocumentRequestJsonV2 = event.payloadAsJsonObject().getJsonObject(INFORMANT_REGISTER_REQUEST_PARAM); + final uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest documentRequestV2 = + jsonObjectToObjectConverter.convert(informantRegisterDocumentRequestJsonV2, uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest.class); - saveRecordedInformantRegister(informantRegisterDocumentRequestJson, documentRequest.getRegisterDate(), - documentRequest.getHearingId(), documentRequest.getProsecutionAuthorityId(), - documentRequest.getProsecutionAuthorityCode(), documentRequest.getProsecutionAuthorityOuCode()); + saveRecordedInformantRegister(informantRegisterDocumentRequestJsonV2, documentRequestV2.getRegisterDate(), + documentRequestV2.getHearingId(), documentRequestV2.getProsecutionAuthorityId(), + documentRequestV2.getProsecutionAuthorityCode(), documentRequestV2.getProsecutionAuthorityOuCode()); } private void saveRecordedInformantRegister(final JsonObject informantRegisterDocumentRequestJson, diff --git a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java index 89301cb56..a10a8a44c 100644 --- a/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java +++ b/results-query/results-query-view/src/test/java/uk/gov/moj/cpp/results/query/view/InformantRegisterDocumentRequestQueryViewTest.java @@ -205,7 +205,7 @@ public void shouldKeepVerdictObjectAsIsWhenAlreadyPresent() { } @Test - public void shouldConvertOffenceVerdictCodeToVerdictObjectForByDateQueryWhenOnlyVerdictCodeIsPresent() { + void shouldConvertOffenceVerdictCodeToVerdictObjectForByDateQueryWhenOnlyVerdictCodeIsPresent() { final LocalDate registerDate = now(); final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); final JsonObject converted = createObjectBuilder() @@ -228,7 +228,7 @@ public void shouldConvertOffenceVerdictCodeToVerdictObjectForByDateQueryWhenOnly } @Test - public void shouldConvertOffenceVerdictCodeToVerdictObjectForByMaterialQueryWhenOnlyVerdictCodeIsPresent() { + void shouldConvertOffenceVerdictCodeToVerdictObjectForByMaterialQueryWhenOnlyVerdictCodeIsPresent() { final UUID fileId = randomUUID(); final InformantRegisterEntity informantRegisterEntity = new InformantRegisterEntity(); final JsonObject converted = createObjectBuilder() From fcde4356564b5f2783d3dc0a662e09725f38efa9 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Fri, 19 Jun 2026 12:04:33 +0100 Subject: [PATCH 18/22] CIMD-3915 : sonar --- runIntegrationTests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runIntegrationTests.sh b/runIntegrationTests.sh index c43f46ffb..7801e8ee6 100755 --- a/runIntegrationTests.sh +++ b/runIntegrationTests.sh @@ -3,7 +3,7 @@ # Script that runs, liquibase, deploys wars and runs integration tests CONTEXT_NAME=results - +# FRAMEWORK_LIBRARIES_VERSION=$(mvn help:evaluate -Dexpression=framework-libraries.version -q -DforceStdout) FRAMEWORK_VERSION=$(mvn help:evaluate -Dexpression=framework.version -q -DforceStdout) EVENT_STORE_VERSION=$(mvn help:evaluate -Dexpression=event-store.version -q -DforceStdout) From 23b2e97163ac6f716605001ae94a8b087d46f29d Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Fri, 19 Jun 2026 12:31:19 +0100 Subject: [PATCH 19/22] CIMD-3915 : sonar --- .../event/InformantRegisterEventListener.java | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java index 355b200e0..8e1fdaed8 100644 --- a/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java +++ b/results-event/results-event-listener/src/main/java/uk/gov/moj/cpp/results/event/InformantRegisterEventListener.java @@ -2,6 +2,7 @@ import static java.time.ZonedDateTime.now; import static java.util.UUID.randomUUID; +import static java.util.stream.Collectors.toList; import static uk.gov.justice.services.core.annotation.Component.EVENT_LISTENER; import static uk.gov.moj.cpp.domains.constant.RegisterStatus.GENERATED; import static uk.gov.moj.cpp.domains.constant.RegisterStatus.NOTIFIED; @@ -12,7 +13,6 @@ import uk.gov.justice.results.courts.InformantRegisterNotified; import uk.gov.justice.results.courts.InformantRegisterNotifiedV2; import uk.gov.justice.results.courts.InformantRegisterGeneratedV2; -import uk.gov.justice.results.courts.InformantRegisterRecordedV2; import uk.gov.justice.services.common.converter.JsonObjectToObjectConverter; import uk.gov.justice.services.core.annotation.Handles; import uk.gov.justice.services.core.annotation.ServiceComponent; @@ -102,22 +102,10 @@ public void generateInformantRegister(final JsonEnvelope event) { final UUID prosecutionAuthorityId = documentRequests.get(0).getProsecutionAuthorityId(); final LocalDate registerDate = documentRequests.get(0).getRegisterDate().toLocalDate(); + final List hearingIds = documentRequests.stream().map(InformantRegisterDocumentRequest::getHearingId).collect(toList()); - final List informantRegisters = informantRegisterRepository.findByProsecutionAuthorityIdAndRegisterDateForStatusRecorded(prosecutionAuthorityId, registerDate); - informantRegisters.forEach(informantRegisterEntity -> { - informantRegisterEntity.setStatus(GENERATED); - informantRegisterEntity.setProcessedOn(currentDateTime); - if(BooleanUtils.isTrue(informantRegisterGenerated.getSystemGenerated())) { - informantRegisterEntity.setGeneratedDate(currentDateTime.toLocalDate()); - informantRegisterEntity.setGeneratedTime(currentDateTime); - } - }); - - documentRequests.stream().map(InformantRegisterDocumentRequest::getHearingId).forEach(hearingId -> { - final List informantRegistersList = informantRegisterRepository.findByHearingIdAndStatusRecorded(hearingId); - informantRegistersList.forEach(informantRegisterEntity -> informantRegisterEntity.setProcessedOn(currentDateTime)); - }); - + markInformantRegistersGenerated(prosecutionAuthorityId, registerDate, + BooleanUtils.isTrue(informantRegisterGenerated.getSystemGenerated()), hearingIds, currentDateTime); } @Handles("results.event.informant-register-generated-v2") @@ -134,23 +122,33 @@ public void generateInformantRegisterV2(final JsonEnvelope event) { final UUID prosecutionAuthorityId = documentRequests.get(0).getProsecutionAuthorityId(); final LocalDate registerDate = documentRequests.get(0).getRegisterDate().toLocalDate(); + final List hearingIds = documentRequests.stream() + .map(uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest::getHearingId) + .collect(toList()); + markInformantRegistersGenerated(prosecutionAuthorityId, registerDate, + BooleanUtils.isTrue(informantRegisterGenerated.getSystemGenerated()), hearingIds, currentDateTime); + } + + private void markInformantRegistersGenerated(final UUID prosecutionAuthorityId, + final LocalDate registerDate, + final boolean systemGenerated, + final List hearingIds, + final ZonedDateTime currentDateTime) { final List informantRegisters = informantRegisterRepository.findByProsecutionAuthorityIdAndRegisterDateForStatusRecorded(prosecutionAuthorityId, registerDate); informantRegisters.forEach(informantRegisterEntity -> { informantRegisterEntity.setStatus(GENERATED); informantRegisterEntity.setProcessedOn(currentDateTime); - if (BooleanUtils.isTrue(informantRegisterGenerated.getSystemGenerated())) { + if (systemGenerated) { informantRegisterEntity.setGeneratedDate(currentDateTime.toLocalDate()); informantRegisterEntity.setGeneratedTime(currentDateTime); } }); - documentRequests.stream() - .map(uk.gov.justice.results.courts.informantRegisterDocument.InformantRegisterDocumentRequest::getHearingId) - .forEach(hearingId -> { - final List informantRegistersList = informantRegisterRepository.findByHearingIdAndStatusRecorded(hearingId); - informantRegistersList.forEach(informantRegisterEntity -> informantRegisterEntity.setProcessedOn(currentDateTime)); - }); + hearingIds.forEach(hearingId -> { + final List informantRegistersList = informantRegisterRepository.findByHearingIdAndStatusRecorded(hearingId); + informantRegistersList.forEach(informantRegisterEntity -> informantRegisterEntity.setProcessedOn(currentDateTime)); + }); } @Handles("results.event.informant-register-notified") @@ -158,12 +156,7 @@ public void notifyInformantRegister(final JsonEnvelope event) { final JsonObject payload = event.payloadAsJsonObject(); final InformantRegisterNotified informantRegisterNotified = jsonObjectToObjectConverter.convert(payload, InformantRegisterNotified.class); final List informantRegisters = informantRegisterRepository.findByProsecutionAuthorityIdAndStatusGenerated(informantRegisterNotified.getProsecutionAuthorityId()); - informantRegisters.forEach(informantRegisterEntity -> { - informantRegisterEntity.setStatus(NOTIFIED); - informantRegisterEntity.setFileId(informantRegisterNotified.getFileId()); - informantRegisterEntity.setProcessedOn(now()); - } - ); + markInformantRegistersNotified(informantRegisters, informantRegisterNotified.getFileId()); } @Handles("results.event.informant-register-notified-v2") @@ -171,9 +164,13 @@ public void notifyInformantRegisterV2(final JsonEnvelope event) { final JsonObject payload = event.payloadAsJsonObject(); final InformantRegisterNotifiedV2 informantRegisterNotified = jsonObjectToObjectConverter.convert(payload, InformantRegisterNotifiedV2.class); final List informantRegisters = informantRegisterRepository.findByProsecutionAuthorityIdAndRegisterDateAndStatusGenerated(informantRegisterNotified.getProsecutionAuthorityId(), informantRegisterNotified.getRegisterDate()); + markInformantRegistersNotified(informantRegisters, informantRegisterNotified.getFileId()); + } + + private void markInformantRegistersNotified(final List informantRegisters, final UUID fileId) { informantRegisters.forEach(informantRegisterEntity -> { informantRegisterEntity.setStatus(NOTIFIED); - informantRegisterEntity.setFileId(informantRegisterNotified.getFileId()); + informantRegisterEntity.setFileId(fileId); informantRegisterEntity.setProcessedOn(now()); } ); From ec47207e1c1690d49755d594c0550e945b14b6f0 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Fri, 19 Jun 2026 14:41:17 +0100 Subject: [PATCH 20/22] CIMD-3915 : fix --- .../results/domain/aggregate/ProsecutionAuthorityAggregate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java b/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java index 315bd14b3..ccbf79ad7 100644 --- a/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java +++ b/results-domain/results-domain-aggregate/src/main/java/uk/gov/moj/cpp/results/domain/aggregate/ProsecutionAuthorityAggregate.java @@ -26,7 +26,7 @@ import java.util.stream.Stream; public class ProsecutionAuthorityAggregate implements Aggregate { - private static final long serialVersionUID = 102L; + private static final long serialVersionUID = -2650621888595752644L; private List informantRegisterRecipients; @Override From 8d8e3a548dad0b22fe1a49c324dcb7bc442ea664 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Fri, 19 Jun 2026 17:08:10 +0100 Subject: [PATCH 21/22] CIMD-3915 : fix --- .../resources/json/schema/informantRegisterDocument/verdict.json | 1 - 1 file changed, 1 deletion(-) diff --git a/results-json/src/main/resources/json/schema/informantRegisterDocument/verdict.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/verdict.json index 0607e4988..74931a3b3 100644 --- a/results-json/src/main/resources/json/schema/informantRegisterDocument/verdict.json +++ b/results-json/src/main/resources/json/schema/informantRegisterDocument/verdict.json @@ -18,7 +18,6 @@ } }, "dependencies": { - "verdictCode": ["verdictDate"], "verdictDate": ["verdictCode"] }, "additionalProperties": false From 70a28c9e39d88a03d36edf5e325849028e240455 Mon Sep 17 00:00:00 2001 From: Erol Kureli <> Date: Fri, 19 Jun 2026 17:19:02 +0100 Subject: [PATCH 22/22] CIMD-3915 : fix --- .../model/VerdictSchemaDependencyTest.java | 15 +++++++-------- .../schema/informantRegisterDocument/verdict.json | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictSchemaDependencyTest.java b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictSchemaDependencyTest.java index 0af309670..5ffa164ba 100644 --- a/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictSchemaDependencyTest.java +++ b/results-domain/results-domain-common/src/test/java/uk/gov/moj/cpp/results/domain/informant/model/VerdictSchemaDependencyTest.java @@ -16,10 +16,11 @@ import org.junit.jupiter.api.Test; /** - * T071 (FR-004): the verdict schema's {@code dependencies} block enforces that {@code verdictCode} - * and {@code verdictDate} are mutually co-dependent. This schema-level constraint is the mechanism - * by which the framework rejects a command carrying {@code verdictCode} without {@code verdictDate} - * at the envelope-validation boundary — no production handler guard is required. + * T071 (FR-004): the verdict schema's {@code dependencies} block enforces that whenever + * {@code verdictDate} is present, {@code verdictCode} must also be present. The reverse is not + * required — {@code verdictCode} may stand alone without a {@code verdictDate}. This schema-level + * constraint is applied by the framework at the envelope-validation boundary — no production + * handler guard is required. * *

    This is verified at the schema layer (not in {@code InformantRegisterHandlerTest}) because the * handler unit test invokes the handler method directly and therefore bypasses the framework's @@ -50,10 +51,8 @@ void verdictCodeWithVerdictDate_shouldValidate() { } @Test - void verdictCodeWithoutVerdictDate_shouldFailValidation() { - final ValidationException exception = assertThrows(ValidationException.class, - () -> schema.validate(new JSONObject().put("verdictCode", "G"))); - assertThat(exception.getMessage(), containsString("verdictDate")); + void verdictCodeWithoutVerdictDate_shouldValidate() { + assertDoesNotThrow(() -> schema.validate(new JSONObject().put("verdictCode", "G"))); } @Test diff --git a/results-json/src/main/resources/json/schema/informantRegisterDocument/verdict.json b/results-json/src/main/resources/json/schema/informantRegisterDocument/verdict.json index 74931a3b3..9f6c452ab 100644 --- a/results-json/src/main/resources/json/schema/informantRegisterDocument/verdict.json +++ b/results-json/src/main/resources/json/schema/informantRegisterDocument/verdict.json @@ -9,7 +9,7 @@ "type": "string" }, "verdictDate": { - "description": "Conviction date in yyyy-MM-dd format. Mandatory when verdictCode is present.", + "description": "Conviction date in yyyy-MM-dd format. Optional, but when present verdictCode is required.", "type": "string" }, "verdictType": {