Skip to content

DD-42938 - Update nightly schedule to get courtcentre and case details using /hearing-cases-for-day - #196

Merged
natrajmolala merged 7 commits into
developfrom
dev/dd-42938
Jul 17, 2026
Merged

DD-42938 - Update nightly schedule to get courtcentre and case details using /hearing-cases-for-day#196
natrajmolala merged 7 commits into
developfrom
dev/dd-42938

Conversation

@natrajmolala

@natrajmolala natrajmolala commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

What

Nightly discovery now fetches hearing cases per calculated date, whitelist-filters them against active court-centre/room configs, dedupes prosecution case IDs across the whole run, and for each unique case
dispatches straight to CHECK_IDPC_AVAILABILITY_ALL_DEFENDANTS (previously it went through a CHECK_CASE_ELIGIBILITY hop, since removed).

Changes

Production code
  • HearingCaseForDay.prosecutionCases — List (was List); the wrapper record HearingCaseProsecutionCase was deleted.
  • HearingClient/HearingClientImpl — new getHearingCasesForDay(date, userId); HearingClientConfig split into get-hearings-* and get-hearing-cases-for-day-* properties (breaking config rename,
    application-clients.yml updated).
  • HearingCaseWhitelistSelector (new) — filters hearing cases by exact (courtCentreId, courtRoomId) match against active configs; null-safe, dedupes identical entries.
  • DiscoveryService.runNightlyDiscovery() — aggregates matched hearing cases across all calculated dates, dedupes caseIds via LinkedHashSet, dispatches
    dispatchCaseDocumentIngestionTasksCheckIdpcAvailability once per unique case. getSystemUserId extracted out to a new EnvironmentUtil (shared, testable in isolation — EnvironmentUtilTest added).
  • JobManagerService — single dispatch method split into dispatchCaseDocumentIngestionTasksGetCasesForHearing (intraday/ingestion-process) and dispatchCaseDocumentIngestionTasksCheckIdpcAvailability (nightly,
    dispatches CHECK_IDPC_AVAILABILITY_ALL_DEFENDANTS directly — the CHECK_CASE_ELIGIBILITY dispatch method that existed briefly is gone).

Tests

  • DiscoveryServiceTest — expanded (~264 lines): per-date fetch, whitelist invocation, per-case dispatch, same-day/cross-date dedup, null/empty prosecution-case handling, failure resilience — all asserting
    against dispatchCaseDocumentIngestionTasksCheckIdpcAvailability.
  • HearingCaseWhitelistSelectorTest, HearingClientImplTest — updated for the List DTO shape.
  • EnvironmentUtilTest (new) — covers missing/invalid system-user-id env var.
  • NightlyDiscoverySchedulerLiveTest + ProgressionQueryApiStub — now stub/assert courtdocumentsearch?caseId=... returning empty documentIndices (replacing the earlier prosecution-case-eligibility
    stub/assertion), matching the new dispatch target end-to-end.

@natrajmolala
natrajmolala requested a review from a team as a code owner July 15, 2026 15:40
}
}

private static JsonObject toJobDataForCaseEligibility(final UUID caseId, final UUID cpSystemUserId) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesnt need to be static

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@natrajmolala
natrajmolala merged commit 285138f into develop Jul 17, 2026
9 of 10 checks passed
@natrajmolala
natrajmolala deleted the dev/dd-42938 branch July 17, 2026 08:45
rkakumanuhmcts added a commit that referenced this pull request Jul 30, 2026
* Discovery Scheduler Configuration: persistence, service, and REST endpoint (#191)

* added discovery schdule initial files from claude

* added repository method through Calude

* updated claude docs through Claude

* Fix CodeQL log-injection finding on DiscoverySchedulerController

Log specific request fields (courtCentreId, courtRoomId, version) instead of
the raw request DTO, addressing the java/log-injection alert flagged on PR #191.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: ravi kakumanu <ravik@ravis-MacBook-Pro-2.local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* [DD-42880] implement nightly scheduler; calculate hearing days ahead and process scheduled ingestions for the dates (#192)

* [DD-42880] implement nightly scheduler; calculate hearing days ahead and process scheduled ingestions for the dates

* DD-42899 - calculate hearingDays ahead and dispatch ingestion workflow tasks for courtcentre configurations (#194)

* calculate hearingDays ahead and dispatch ingestion workflow tasks for courtcentre configurations

* removed Transactional to limit transaction to creating/inserting the jobmanager task

* throw exception when no value for env variable CASEDOCUMENTKNOWLEDGE_SYSTEM_USER_ID

* tidyup

* DD-42807 - Manual by-case ingestion endpoint (POST /ingestions/start-by-case) (#195)

* DD-42807 - add manual by-case ingestion endpoint (POST /ingestions/start-by-case)

Adds a synchronous entry point for the "Process IDPC" button on the AI Search
page: given a single caseId it runs eligibility and IDPC-availability checks
inline (via CheckCaseEligibilityTask/CheckIdpcAvailabilityAllDefendantsTask,
same task classes the scheduled flow uses) and only responds once the outcome
is known - STARTED, NOT_REQUIRED, or FAILED. Downstream JobManager tasks run
at HIGH priority so manual clicks jump ahead of scheduled ingestion; the
existing /ingestions/start endpoint is unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* DD-42807 - address code review findings on manual ingestion endpoint

- Split the shared NOT_REQUIRED message: "case not eligible" now gets its
  own accurate message instead of reusing the "no newer IDPC version
  available, Answers already exists" text, which was misleading when the
  case was never found or has no defendants.
- Strip CTX_SYNCHRONOUS_INVOCATION_KEY from job data before it reaches the
  dispatched RETRIEVE_MATERIAL_AND_UPLOAD job, so the sync-only flag never
  rides into the async job queue.
- Add a test asserting HIGH priority actually propagates through to the
  dispatched retrieval job (the only point priority takes effect), and that
  the synchronous-invocation flag is stripped from its job data.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* DD-42807 - move eligibility/IDPC-availability rules into plain service classes

Per review comment: reverts CheckCaseEligibilityTask and
CheckIdpcAvailabilityAllDefendantsTask to thin JobManager task wrappers, and
moves the reusable business logic into CaseEligibilityService and
IdpcAvailabilityService respectively. Both the async scheduled flow (via the
task classes) and the synchronous manual "Process IDPC" flow (via
IngestionProcessorByCaseService) now call these services directly as plain
methods, rather than IngestionProcessorByCaseService invoking
CheckCaseEligibilityTask.execute(ExecutionInfo) through the task-manager
contract.

This removes the need for CTX_SYNCHRONOUS_INVOCATION_KEY entirely: since
IngestionProcessorByCaseService never touches the task classes' execute()
methods, there is no risk of the async job queue being triggered twice for
the same case, so the flag (and its associated stripping-before-dispatch
logic) is deleted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* DD-42807 - drop redundant case-eligibility step, share retrieval job-data building

CaseEligibilityService/CheckCaseEligibilityTask added a round-trip that
duplicated what IdpcAvailabilityService already establishes: an empty
document list means nothing to ingest, whether that's because the case
doesn't exist, has no defendants, or is already up to date. Both the
scheduled flow (GetCasesForHearingTask now dispatches
CHECK_IDPC_AVAILABILITY_ALL_DEFENDANTS directly) and the manual by-case
flow (IngestionProcessorByCaseService) call IdpcAvailabilityService
directly instead.

Extracted RetrieveMaterialAndUploadJobDataService to build the
per-document RETRIEVE_MATERIAL_AND_UPLOAD job data once, shared by both
call sites, instead of duplicating that logic in two private dispatch
methods.

Removed the now-dead prosecution-case-eligibility client method, its
DTOs and config, and the wiremock fixtures backing it; added a
replacement failure stub on the court-document-search endpoint so the
by-case FAILED integration scenario still exercises a real error path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* DD-42807 - inline user-id header lookup in startIngestionProcessByCase

The intermediate headerName variable held the header's name, not the
user id, which read as mislabeled per review. Inlining the lookup
removes the confusion entirely — cppuid is the only variable left, and
it holds the actual value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: ravi kakumanu <ravik@ravis-MacBook-Pro-2.local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* DD-42938 - Update nightly schedule to get courtcentre and case details using /hearing-cases-for-day (#196)

* get hearing cases for day

* passing integration tests

* Updated integration test in NightlyDiscoverySchedulerLiveTes to assert CHECK_CASE_ELIGIBILITY task executed

* Dedupe cases across the hearing days

* removed static reference from the method

* updated HearingClientImpl and response dto as per the response - returns list of caseIds

* rebase with develop; discovery service now dispatches task CHECK_IDPC_AVAILABILITY_ALL_DEFENDANTS

* Add a toggle to the /usage/hearing-cases-for-day endpoint until this is available (#198)

* enable feature toggle to hit new hearing endpoint (#199)

* added env var CP_CDK_SCHEDULER_NIGHTLY_DISCOVERY_CRON for nightly discovery cron (#200)

* Check for an existing answer before reporting NOT_REQUIRED for by-case ingestion (#202)

startIngestionProcess in IngestionProcessorByCaseService returned
IngestionProcessPhase.NOT_REQUIRED whenever no newer IDPC document was
found, even when no answer had ever been generated for the case (e.g.
answer generation failed after a previous ingestion). It now also
checks CaseQueryStatusRepository for an ANSWER_AVAILABLE entry: only
returns NOT_REQUIRED when an answer already exists, otherwise returns
STARTED with a distinct message and dispatches nothing new.

Fixes a latent bug surfaced by this change: CaseQueryStatusRepository's
findByCaseId/findByCaseIdAndQueryId were broken derived queries that
resolved against the entity's plain caseId getter instead of the
embedded id, throwing UnknownPathException the first time they were
actually invoked. Both now use explicit JPQL through
caseQueryStatusId.

Co-authored-by: ravi kakumanu <ravik@ravis-MacBook-Pro-2.local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* Bump api-cp-ai-rag contract dependency to 0.0.15 (#203)

Verified the only contract change between 0.0.12 and 0.0.15 is removal
of the unused DocumentStatusNotAvailable model; every API and model
class actually used by this service is unchanged. Unit and integration
tests pass against the new version.

Co-authored-by: ravi kakumanu <ravik@ravis-MacBook-Pro-2.local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* externalise nightly scheduler lock config (#204)

* Scope by-case ingestion answer check to the latest IDPC document (#205)

* Scope hasAnswerAvailable check to the case's latest IDPC document

Previously any ANSWER_AVAILABLE case_query_status row for a case (regardless
of which document it was generated against) satisfied the check, so a stale
answer from a superseded IDPC version could wrongly report NOT_REQUIRED.
Now resolves the case's latest doc_id (case_documents, ordered by
uploaded_at) and requires at least one answer recorded against that doc_id.

Also adds a caseId/cppuid info log to CheckIdpcAvailabilityAllDefendantsTask
for traceability.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Combine hasAnswerAvailable lookup into a single DB round trip

Address review comment: resolving the latest doc_id and checking its
answer status were two separate repository calls. Replaced with one native
query (CaseQueryStatusRepository#existsAnswerAvailableForLatestDoc) that
resolves the latest doc_id as a correlated subquery and checks it via
EXISTS in a single call, removing the now-unused CaseDocumentRepository
dependency from IngestionProcessorByCaseService.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: ravi kakumanu <ravik@ravis-MacBook-Pro-2.local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: ravi kakumanu <ravik@ravis-MacBook-Pro-2.local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Natraj Molala <natraj.molala3@hmcts.net>
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.

3 participants