-
[OPIK-5217] [BE] Add project_id column to alerts for project-scoped alert triggers (#5825)
发布于
2026-03-25 16:54:48 +00:00 - [OPIK-5217] [BE] Add project_id column to alerts for project-scoped alert triggers
- Add
project_idcolumn (CHAR(36) NULL) toalertsMySQL table via Liquibase migration - Add
projectIdfield toAlertrecord and propagate throughAlertDAO/AlertService - Add
ProjectAlertsResourceto expose project-scoped alert endpoints (/v1/private/projects/{projectId}/alerts) - Update
AlertEventEvaluationService.isWithinProjectScopeto check both the newprojectIdcolumn and the legacyscope:projecttrigger config (backward-compatible) - Update
ExperimentServiceandPromptServiceto group entities byprojectIdand fire per-projectAlertEventinstances so project-scoped alerts fire correctly - Add
AlertResourceClientsupport forprojectIdin test utilities - Add integration tests for project-scoped alert triggering via
projectIdcolumn for guardrail, experiment-finished, and prompt-created events
Implements OPIK-5217
- Revision 2: Extract shared alert comparison logic into AlertAssertions utility class
- Add AlertAssertions in com.comet.opik.api.resources.utils.alerts with static
compareAlerts and prepareForComparison methods and shared ignored-field constants - Remove duplicate comparison code from AlertResourceTest and ProjectAlertsResourceTest
- Update ProjectAlertsResourceTest to compare full Alert objects instead of isolated properties
- Revision 3: Fix webhook secret token masking, null triggerConfigs, and Podam project_ids format
- Add @JsonSerialize(MaskedSecretTokenSerializer) to Webhook.secretToken so masking
happens via Jackson on the response path instead of manual decrypt+mask in resource layer - Remove manual maskSecretToken() helpers from AlertResource and ProjectAlertsResource
- Add MaskedSecretTokenSerializer with try-catch fallback for plain-text tokens
- Extract AlertScopeUtils.collectProjectIds() shared helper used by both
AlertEventEvaluationService and MetricsAlertJob to avoid duplicated logic - Move MetricsAlertJob to api/resources/v1/jobs package for consistency
- Guard against null triggerConfigs in AlertScopeUtils using Optional
- Fix AlertResourceClient to restore original secretToken in ObjectNode before sending
(avoids masking on the test client request path) - Fix generateAlert() to serialize SCOPE_PROJECT project_ids as JSON array
(Podam was generating plain UUID strings, causing JsonParseException in collectProjectIds) - Fix AlertAssertions.prepareForComparison null-safety for nullable triggerConfigs
- Revision 3: Address PR review comments on AlertScopeUtils and MaskedSecretTokenSerializer
- AlertScopeUtils.collectProjectIds: add JSON-array guard before calling
JsonUtils.readCollectionValue; fall back to comma-split for legacy bare-UUID
or comma-separated strings - MaskedSecretTokenSerializer.serialize: log decrypt failures at DEBUG level
instead of silently swallowing the exception
- Revision 3: Fix ProjectAlertsResourceTest and clean up AlertScopeUtils
- Add missing Set import to ProjectAlertsResourceTest
- Fix SCOPE_PROJECT config value to use JsonUtils.writeValueAsString(Set.of(projectId))
to match the established pattern from AlertResourceTest and main branch - Revert unnecessary bare-UUID fallback from AlertScopeUtils.collectProjectIds;
project_ids has always been stored as a JSON array in production
- Revision 4: Restore legacy bare-UUID fallback in AlertScopeUtils
Rows created before the JSON array format was adopted may store a bare
UUID (or comma-separated UUIDs) in the SCOPE_PROJECT config value.
Without the startsWith("[") guard, Jackson tried to parse those as
numeric literals and threw JsonParseException (leading zeroes not allowed).- Revert "Revision 4: Restore legacy bare-UUID fallback in AlertScopeUtils"
This reverts commit ca50c2170e37f07351958f40af2f58bbe9646bd9.
- Revision 5: Fix null vs empty list mismatch in generateAlertWithoutProjectScope
When PODAM-generated triggers only had SCOPE_PROJECT configs, filtering
them out produced an empty list. The API skips saving empty config lists,
so the DB returns null on retrieval. Normalize empty filtered lists to null
to match actual DB/mapper behavior.- Revision 3: Accept and update project_id in PUT alert when no SCOPE_PROJECT triggers
- update() now validates incoming request via validateNoProjectScopeConflict
before any field resolution; project_id is accepted and stored when the
update carries no SCOPE_PROJECT trigger configs - Added generateAlertForProject / generateAlertForProject helpers and
stripScopeProjectConfigs in test to isolate scenarios cleanly - Three new UpdateAlert tests: project_id updated when no SCOPE_PROJECT,
project_id cleared when SCOPE_PROJECT present, 400 when both present
- Revision 3: Address PR review comments
- AlertDAO: inline COUNT query string (no reuse needed)
- PromptService: use static import for groupingBy instead of FQN
- MaskedSecretTokenSerializer: early return for null, mask raw value on decrypt failure
- Migration 000060: fix rollback order (drop index before column)
下载附件