Release 20260508 hotfix#412
Conversation
…ializer The UniqueConstraint(integration, action) added in #406 caused DRF to auto-attach a UniqueTogetherValidator to IntegrationConfigurationCreate UpdateSerializer. That validator forces `integration` and `action` to be required regardless of their declared `required=False`, which broke PATCH /v2/integrations/{id}/ from the portal — the portal omits `integration` on each configuration item because it is implied by the URL. The parent IntegrationCreateUpdateSerializer.update() already injects `integration` from the URL post-validation and uses update_or_create on the configuration id, and the DB-level UniqueConstraint enforces uniqueness on insert. The serializer-level UniqueTogetherValidator is redundant. Adds a regression test mirroring the portal's payload shape ({id, action, data} with no `integration`). Fixes GUNDI-5313 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses PR feedback. With the auto-generated UniqueTogetherValidator
disabled (previous commit), the (integration, action) UniqueConstraint
could be hit at insert time and surface as an unhandled 500. Add
explicit duplicate checks in IntegrationCreateUpdateSerializer.validate():
1. On PATCH, a new (no-id) configuration item for an action that already
has a config raises 400 with a clear message ("A configuration for
action X already exists. Include 'id' in the payload to update it.").
2. Two configuration items in the same request targeting the same action
raises 400 with "Duplicate configurations for action X in the request."
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lidator fix: PATCH /v2/integrations/ rejects nested configurations (GUNDI-5313)
There was a problem hiding this comment.
Pull request overview
This PR adjusts the integrations v2 update/create validation flow to better match the portal’s payload shape (configuration items omitting integration) and to proactively reject conflicting configuration updates with clearer 400-level validation errors.
Changes:
- Disabled DRF’s auto-generated
UniqueTogetherValidatorforIntegrationConfigurationCreateUpdateSerializerto avoid incorrectly requiringintegrationin nested configuration payloads. - Added request-level validation in
IntegrationCreateUpdateSerializer.validate()to reject (a) adding a new configuration for an action that already has one and (b) duplicate actions in the same request payload. - Added targeted API tests for the above scenarios.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
cdip_admin/api/v2/serializers.py |
Disables nested unique-together validator and adds additional validation for PATCH/create payloads (duplicate actions, conflicting new configs). |
cdip_admin/api/v2/tests/test_integrations_api.py |
Adds regression and validation tests for portal-shaped payloads and duplicate/conflicting configuration updates. |
Comments suppressed due to low confidence (1)
cdip_admin/api/v2/serializers.py:532
- When an item includes an
id, the code doesIntegrationConfiguration.objects.get(id=...)without scoping toself.instance(the integration being patched). This allows a client to reference a configuration from a different integration (and potentially reassign it inupdate_or_create), and a nonexistent id will raiseDoesNotExistand surface as a 500. Prefer fetching withIntegrationConfiguration.objects.get(id=..., integration=self.instance)(whenself.instanceis set) and raising aValidationErrorif not found/belongs to a different integration.
if self.instance and "id" in configuration: # Integration Update
action = IntegrationConfiguration.objects.get(id=configuration["id"]).action
else: # Create a new integration or new config
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…action on id-update; tighten test assertions) Three Copilot review comments on #412: 1. `IntegrationConfiguration.objects.get(id=...)` in validate() wasn't scoped to the integration being patched. A nonexistent id surfaced as a 500 from DoesNotExist, and an id belonging to a different integration was accepted and later repointed via update_or_create's defaults. Scope the lookup to `self.instance.configurations` and raise a clean 400 with "Configuration '...' was not found on this integration." 2. `update()` passed the full `config_data` (including any payload `action`) as `defaults` to `update_or_create`. When `id` was present that could repoint the row's action — colliding with the (integration, action) UniqueConstraint and bypassing the schema validation in validate(). Pop `action` from defaults when `id` is present so id-updates can only change `data`. 3. Tests asserted on `str(response.json())` — brittle. Assert against `response.json()["non_field_errors"][0]` instead. Added three regression tests: - unknown config id → 400 (not 500) - id belonging to a different integration → 400, original config untouched - payload `action` alongside `id` is ignored (action stays put) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Addressing the three review comments here: #413 (into Summary of fixes:
Three new regression tests added: unknown id, cross-integration id, and |
fix: address Copilot review comments from PR #412
…[release-20260508] (#422) v12 uses direct gcloud container clusters get-credentials instead of Fleet Connect Gateway, fixing the deploy_prod_legacy failure caused by cdip-prod01 not being registered in the serca-tools Fleet.
@marianobrc this is a hotfix I pushed to rectify a bug I introduced in my last PR (re: adding a uniqueness constraint on IntegrationConfiguration).
...and here is Claude's explanation below:
This pull request improves the validation logic for integration configuration updates, ensuring more user-friendly error handling and preventing duplicate or conflicting configurations. It also adds targeted tests to cover these scenarios and disables redundant validation that conflicted with the intended API design.
Validation improvements:
UniqueTogetherValidatorinIntegrationConfigurationCreateUpdateSerializer.Metato prevent it from incorrectly requiring theintegrationfield, since this field is implied by the URL and handled elsewhere.validatemethod inIntegrationCreateUpdateSerializerto:update()so anactionechoed back alongside anidcannot silently repoint an existing configuration row — it's popped before reachingupdate_or_create. Pairs with the validation above so a row's(integration, action)identity is effectively immutable once created.Test coverage:
integrationfield in configuration items, matching the expected portal behavior.idthat belongs to a different integration returns 400 (rather than silently repointing the row).id+ a differentactionis accepted, but the row's action is NOT repointed — onlydataupdates.CI/CD pipeline migration (DASOPS-2057)
Beyond the serializer hotfix, this release branch also picks up the operational work tracked under DASOPS-2057. Splitting it out at this point isn't practical — the release-20260508 branch is the cutoff for this release and the CI/CD migration needs to ride along — so calling it out here explicitly:
serca-artifact-registry(single regional registry) instead of the legacy registry path.approve_prodjob now guards production deploys (previously implicit via branch protections).deploy_prod_legacyretained as a fallback path with direct GKE credentials in case the ArgoCD-driven prod deploy needs to be bypassed during the cutover window.Operational prerequisites already in place: the
serca-artifact-registryGCP project + IAM bindings for the workflow's service account, and the GitHub environment(s) referenced byapprove_prod. No new application secrets are required by this PR.