Skip to content

fix(ccsph2n): court-calendar courtscheduler search — de-duplicate per day & exclude drafts [SPRDT-1164]#39

Open
ozturkmenb wants to merge 5 commits into
team/ccsph2nfrom
dev/SPRDT-1164-courtcalendar-pathA-fix
Open

fix(ccsph2n): court-calendar courtscheduler search — de-duplicate per day & exclude drafts [SPRDT-1164]#39
ozturkmenb wants to merge 5 commits into
team/ccsph2nfrom
dev/SPRDT-1164-courtcalendar-pathA-fix

Conversation

@ozturkmenb

Copy link
Copy Markdown

What & why

The Crown court-calendar range-search (GET .../listing/hearings/range-search, court.calendar media) has a courtscheduler-backed code path (RangeSearchQuery PATH A → getCourtSchedulerHearings), taken when a specific session + ouCode + allocated=true are supplied. Two defects there produced the reported UI bug (blank hearing start-times, duplicate rows, undefined courtroom):

  1. Aliasing / collapse — the enrichment kept one Hearing instance per id and mutated + re-added that same reference for every returned hearing-day; the per-row flatten also mutates the shared properties JsonNode. A multi-day hearing therefore collapsed onto its last day (e.g. a 3-day hearing rendered 3× all on day 3).
  2. Draft leak — PATH A is entered only when allocated=true, yet it never applied an allocated filter, so draft/unallocated courtscheduler sessions leaked into the allocated view (and inflated the result count).

Full RCA: https://tools.hmcts.net/confluence/spaces/LPT/pages/1990370971 · Decision: ADR-006

Changes

  • Hearing (viewstore entity): copy constructor that deep-copies properties so per-day copies can't corrupt each other during flatten.
  • RangeSearchQuery.enrichAllCourtSchedulerHearingIdsIntoHearings: build one Hearing per IdResponse (distinct per-day rows) and skip non-allocated base hearings.
  • RangeSearchQuery.getCourtSchedulerHearings: report the de-duplicated/allocated row count when the result set fits on one page; keep the courtscheduler total for genuinely paged results (pagination-safe — RangeSearchQueryForMagistratesIT multi-page count still green).

ouCode routing is deliberately left unchanged (deferred; the retained routing coupling is documented in ADR-006 §6).

Tests

  • Unit (RangeSearchQueryTest, +2, written test-first RED→GREEN): per-day split + draft exclusion. Full class 33/33.
  • Integration (RangeSearchQueryForCourtCalendarIT, +1): crownCourtSchedulerPathKeepsEachHearingDayDistinct drives PATH A end-to-end for CROWN. Full listing IT suite run locally: 257 passed, 0 failures, 0 errors (3 pre-existing skips); RangeSearchQueryForMagistratesIT still green.
  • New IT stub helper CourtSchedulerServiceStub.stubGetHearingIdsWithBody(...).

Verification

  • mvn clean + ./runIntegrationTests.sh full suite: BUILD SUCCESS, 257/257.
  • SonarQube quality gate: not run locally (Sonar host unreachable from this environment, and SonarQube CE does not do PR analysis) — will run in the context-verify CI pipeline on this PR.

Ticket: SPRDT-1164

… day and exclude drafts [SPRDT-1164]

The court-calendar range-search courtscheduler path (RangeSearchQuery PATH A,
getCourtSchedulerHearings) mutated and re-added a single shared Hearing instance per
returned hearing-day, collapsing multi-day hearings onto their last day; it also never
applied the allocated filter, so draft/unallocated sessions leaked into the allocated view.

- Hearing: add copy constructor that deep-copies properties (per-row flatten mutates it),
  so day-copies cannot corrupt each other.
- RangeSearchQuery.enrichAllCourtSchedulerHearingIdsIntoHearings: build one Hearing per
  IdResponse (distinct per-day rows) and skip non-allocated base hearings.
- getCourtSchedulerHearings: report the de-duplicated/allocated row count when the result
  set fits on one page; keep the courtscheduler total for genuinely paged results.
- Unit tests for per-day split and draft exclusion; CROWN PATH-A integration test.

ouCode routing is deliberately left unchanged (deferred).
@ozturkmenb ozturkmenb requested a review from a team as a code owner July 14, 2026 02:39
@cpp-github-management

Copy link
Copy Markdown

Failed

@cpp-github-management

Copy link
Copy Markdown

Passed

…er + listing-side hearingType filter + dedupe [SPRDT-1164]

Reworks the court-calendar range-search courtscheduler path (RangeSearchQuery PATH A):
- Draft (unallocated) court-schedule sessions are now excluded at source: when the request is
  allocated=true, isDraft=false is passed to courtscheduler get.hearing.ids. The previous
  listing-side allocated filter and single-page count adjustment are removed (single filter home).
  Pagination/count are left as main: the caller's pageSize is forwarded and the courtscheduler
  results count is passed straight through.
- hearingType is filtered listing-side (viewstore typeId), since courtscheduler has no hearing type.
- hearingIds are de-duplicated before the viewstore fetch.
- the per-day Hearing copy (multi-day split) is retained.
The plain (non-court-calendar) range-search path is unchanged (isDraft/hearingType passed as null).

CourtSchedulerServiceAdapter.getCourtSchedulerHearings gains a nullable isDraft param, added to the
courtscheduler query only when non-null.

Tests: RangeSearchQueryTest (+5), CourtSchedulerServiceAdapterTest (+2); court-calendar IT
shouldRouteCourtCalendarPathAByCourtSessionAndFilterByHearingType covers Any/AM/AD and the
hearingType filter (PATH A + PATH B routing). Full listing IT suite: 258 passed, 0 failures, 3 skipped.
@ozturkmenb

Copy link
Copy Markdown
Author

Follow-up on SPRDT-1164 — pushed 6886262c2 reworking court-calendar PATH A so filter ownership is split per the "one home per filter" rule:

  • Draft exclusion → courtscheduler-side: isDraft=false is now sent to get.hearing.ids for the allocated view. The previous listing-side allocated filter and the single-page count adjustment are removed; pagination/count are left as main (caller pageSize forwarded, courtscheduler results passed through).
  • hearingType → listing-side (viewstore typeId), since courtscheduler has no hearing type.
  • hearingIds deduped before the viewstore fetch; per-day multi-day split retained.
  • Plain (non-court-calendar) range-search path unchanged.

Companion courtscheduler PR (adds the isDraft param; deploys together as team/ccsph2n + team/ccsph2): hmcts/cpp-context-listing-courtscheduler#869

Tests: RangeSearchQueryTest +5, CourtSchedulerServiceAdapterTest +2; new IT shouldRouteCourtCalendarPathAByCourtSessionAndFilterByHearingType (Any/AM/AD + hearingType, PATH A & B). Full listing IT suite: 258 passed, 0 failures, 3 skipped.

@cpp-github-management

Copy link
Copy Markdown

Failed

…raft filter, drop duplicate isDraft [SPRDT-1164]

The court-calendar PATH A draft filter now rides the courtscheduler get.hearing.ids endpoint's
pre-existing `status` param (DRAFT/FINAL/ALL) instead of a dedicated isDraft flag: PATH A sends
status=FINAL when allocated=true. CourtSchedulerServiceAdapter.getCourtSchedulerHearings takes a
nullable `status` (added to the query only when non-null); the pre-existing IS_DRAFT constant is left
untouched (used by unrelated wire fields). hearingType (listing-side) filter, dedupe, per-day copy and
main-parity pagination/count are unchanged.

Behaviourally identical to the previous isDraft=false (FINAL == non-draft); removes duplicate API surface.

Tests: CourtSchedulerServiceAdapterTest 24/24 (asserts status=FINAL on the query), RangeSearchQueryTest
38/38. Court-calendar IT unchanged (stubs on courtSession) — full listing IT suite still 258/0.
@cpp-github-management

Copy link
Copy Markdown

Failed

…onar java:S2699) [SPRDT-1164]

shouldRouteCourtCalendarPathAByCourtSessionAndFilterByHearingType relied solely on
pollWithDefaults(...).until(...) matchers, which SonarQube's java:S2699 does not recognise as
assertions. Capture the final polled ResponseData and assert on its payload explicitly.
@cpp-github-management

Copy link
Copy Markdown

Passed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants