-
[OPIK-6852] [BE] Agent Insights jobs CRUD (#7087)
发布于
2026-06-18 11:05:41 +00:00 - [OPIK-6852] [BE] Agent Insights jobs CRUD
Per-(workspace, project) configuration for the Agent Insights report in
apps/opik-backend, built from scratch mirroring the Ollie daily-report feature
(ReportPreferenceDAO / ReportService / ReportsResource).- agent_insights_jobs table (migration 000082) + AgentInsightsJobDAO
- AgentInsightsJobService: enable (upsert -> enabled + immediate first run), get,
disable (status flip, never delete), findAllEnabled (consumed by OPIK-6853 cron) - AgentInsightsJobResource at /v1/private/agent-insights/jobs:
POST /jobs (201 first enable / 200 if existed), GET /jobs?project_id (404 if none),
POST /jobs/disable (204 / 404). workspace_id always from the auth context. - DAO integration test + JAX-RS resource tests + trigger test (idempotency,
workspace isolation, 404 paths, immediate-run-once).
The immediate run on enable goes through AgentInsightsReportClient, bound to a
no-op default; the real Platform-BE client is OPIK-6854's deliverable and replaces
this binding (the immediate run is inert until then).Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- [OPIK-6852] Address review: plural resource naming + validation test + Location header
- Rename AgentInsightsJobResource/Test -> AgentInsightsJobsResource(Test) per the
opik-backend plural-resource naming convention - Document the Location header on the 201 response
- Add a 400 test for enable with a missing project_id (AgentInsightsJobsResourceTest)
Not applied: deriving
createdfrom the upsert affected-row count. The project's MySQL
connector returns matched-not-changed rows, so ON DUPLICATE KEY UPDATE reports 1 on the
duplicate path too, breaking the 201-vs-200 distinction. Kept the in-transaction pre-read.Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- [OPIK-6852] Fix missing-project_id test to exercise the real validation path (422)
The previous test posted the JSON string "{}" via Entity.json(String), which the client
serializes Jackson-quoted -> a malformed-body 400, not the @NotNull validation. Post a
proper empty object (Map.of()) so project_id is null and assert 422 (opik's bean-validation
status), genuinely covering the missing-project_id case.Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- [OPIK-6852] Split scheduler endpoints: create / PATCH update / trigger
Reshape the agent-insights jobs resource per review feedback:
- POST /jobs now only CREATES (idempotent upsert, 201/200) and no longer triggers an
immediate run - POST /jobs/disable -> PATCH /jobs?project_id= : partial update of any mutable field
(today: status), returns the updated job (200), 404 when absent - new POST /jobs/trigger {project_id} -> 202 : runs the immediate report on demand
(404 when the job doesn't exist)
Service: create / update / triggerNow; DAO disable -> general updateStatus; new
AgentInsightsJobUpdate request DTO. Tests reworked accordingly (resource 8, dao 6,
trigger 1; the trigger test now asserts create does NOT fire and the trigger endpoint does).Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- [OPIK-6852] Drop the agent_insights_jobs status index (review)
Per review: the status index isn't needed (low-cardinality enum; findAllEnabled scans
the small enabled set anyway).Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- [OPIK-6852] Renumber migration 000082 -> 000083 (prefix conflict with main)
main merged 000082_create_agent_insights_issues_tables.sql while this PR was open, so the
jobs-table migration now collides on the 000082 prefix. Renumber to the next free prefix
(000083) and update the changeset id. As a new changeset it also sidesteps the in-place
checksum concern on the deployed PR env.Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- [OPIK-6852] Review: project_id as path var, create-409, insert-only DAO
Address Thiago/Boris review (CRUD-side):
- project_id is now a path variable: POST/GET/PATCH/POST-trigger on /jobs/{projectId}
(drops the request body / query param; removes AgentInsightsJobRequest) - create() returns 201 and throws 409 (EntityAlreadyExistsException) if a job already
exists for the (workspace, project) — no longer an upsert - DAO enable -> create: plain INSERT (insert-only; duplicate violates the unique key)
- AgentInsightsJob is response-only: project_id marked @Schema(requiredMode = REQUIRED)
- trimmed verbose comments
Scheduler bits (trigger, findAllEnabled, last_triggered_at/markTriggered, report client)
intentionally kept here for now; auth stays PROJECT_DATA_VIEW (documented in review reply).
Tests reworked: resource 7, dao 6 (create insert-only + duplicate-throws), trigger 1.Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- [OPIK-6852] Remove scheduler-only DB bits (defer to OPIK-6853)
Per review, drop the bits that belong to the cron PR (OPIK-6853), not CRUD:
- findAllEnabled (DAO + Service) and the EnabledJob projection
- markTriggered (DAO) and its call in triggerImmediate
- last_triggered_at (migration column + AgentInsightsJob field)
The manual trigger endpoint stays (fire-and-forget); it no longer records a run time.
Tests trimmed accordingly (DAO 4, resource 7, trigger 1).Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- [OPIK-6852] Defer trigger execution to OPIK-6853; drop report client + DAO/trigger tests
Address the remaining Thiago/Boris review:
- triggerNow no longer runs synchronously (Thiago's blocker: no inline/unbounded work).
It validates the job exists (404) and is a no-op placeholder with a TODO(OPIK-6853) to
enqueue the run on a bounded async queue. Endpoint kept, returns 202. - Remove AgentInsightsReportClient + NoopAgentInsightsReportClient (no longer used here;
the real client + queue land in OPIK-6853). - Remove AgentInsightsJobDAOTest and AgentInsightsJobTriggerTest — the resource
integration test (AgentInsightsJobsResourceTest) covers create/get/update/trigger. - AgentInsightsJob.projectId: @Schema(READ_ONLY + REQUIRED) (response-only model).
#7087 is now pure CRUD; scheduler/trigger execution belongs to OPIK-6853 (#7105).
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- [OPIK-6852] create(): race-safe 409 via EntityConstraintHandler + reuse validateProjectIdExists
Address Baz:
- map the unique-key violation to 409 with EntityConstraintHandler.handle(...).withError(...)
so concurrent creates return 409 instead of a 500 (drops the racy check-then-insert) - reuse ProjectService.validateProjectIdExists(projectId, workspaceId) for the 404 instead
of a hand-rolled findByIds().isEmpty() check
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
- [OPIK-6852] De-reactify AgentInsightsJobService — plain sync state-DB calls
Per Boris review: jobs CRUD hits MySQL (state DB), not ClickHouse, so the reactor
wrapping (Mono.fromCallable + subscribeOn(boundedElastic) + resource .block()/
.contextWrite) was unnecessary ceremony. Return plain types and call
transactionTemplate.inTransaction(...) directly, matching AlertService/PromptService.Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Co-authored-by: Claude Opus 4.8 (1M context) noreply@anthropic.com
下载附件