Skip to content

fix(db): match SQL constraint behavior on MongoDB - #1260

Open
madhavilosetty-intel wants to merge 1 commit into
mainfrom
fix/mongo-constraint-parity
Open

fix(db): match SQL constraint behavior on MongoDB#1260
madhavilosetty-intel wants to merge 1 commit into
mainfrom
fix/mongo-constraint-parity

Conversation

@madhavilosetty-intel

@madhavilosetty-intel madhavilosetty-intel commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

What

Two repository behaviors differed between the SQL backends and MongoDB, so the same request returned a different status depending on which database Console was configured with. CLAUDE.md treats that as a bug: the use case has no way to know which backend it got.

1. Duplicate key on update. Every Mongo Insert mapped a duplicate-key error to NotUniqueError; no Mongo Update did. A unique-index collision on update surfaced as a generic DatabaseError, so the handler answered 400 where Postgres and SQLite answer 409.

PATCH /api/v1/admin/domains   (suffix already owned by another domain)
  postgres / sqlite -> 409      mongo -> 400

2. Referential integrity on wireless-profile delete. The profiles_wirelessconfigs foreign key rejects this on Postgres and SQLite. Mongo has no constraints, so the delete succeeded, leaving the AMT profile pointing at a wireless profile that no longer exists.

DELETE /api/v1/admin/wirelessconfigs/office   (referenced by an AMT profile)
  postgres / sqlite -> 400 foreign key violation      mongo -> 204, row gone

How

  • The duplicate-key mapping is added to Update in all six Mongo repositories, matching what each Insert already does.
  • WirelessRepo.Delete looks for a referencing profiles_wirelessconfigs document before deleting. This is what RPS itself did — src/data/postgres/tables/wirelessProfiles.ts runs SELECT 1 FROM profiles_wirelessconfigs ... and throws 'Foreign key violation' rather than relying on the constraint — so this is parity with the service Console replaces, not a new invention.
  • ForeignKeyViolationError moves from sqldb to repoerrors so both backends raise one type and the controller's errors.As check works for either. sqldb keeps the name as a type alias, so no other package changes.

Its doc comment previously said the error "has no place in the cross-backend error vocabulary" because Mongo lacks constraints. That reasoning is what left the divergence in place, so the comment is corrected rather than worked around. Flagging it explicitly for review — it reverses a documented design decision.

Note for the reviewer: CodeQL

CodeQL raises go/sql-injection (high) on the new FindOne in wificonfig.go. It is the same pattern as every other query in this package: the value is passed as a BSON value, not concatenated into a query string, so it cannot alter the query structure — and Delete already rejects anything that fails identifierRegex before the query runs.

This repository has 20+ alerts for this exact rule in this exact package already dismissed as "false positive", including two in wificonfig.go itself (alerts #166 and #167, on the neighbouring Delete and Update). This one is alert #238 and needs the same disposition — I have not dismissed it, since that is a maintainer call.

Testing

  • go build ./..., go vet ./..., gofmt -s -l — clean
  • go test -count=1 ./... and go test -race -count=1 ./... — pass
  • New, using the existing drivertest wire-level mock:
    • Test{CIRA,Device,Domain,IEEE8021x,Profile,Wireless}Repo_Update_DuplicateReturnsNotUniqueError — one per repository, so every branch this PR adds is covered
    • TestWirelessRepo_Delete_ReferencedByProfileIsRejected and TestWirelessRepo_Delete_ReferenceLookupFailurePreventsDelete
  • End-to-end against a real MongoDB via the compose stack: the RPS Postman collection goes from 4 failures to 0 with these changes

Found while re-enabling the RPS Postman collection, which has not run in CI since #217. The CI change that turns it back on follows in a separate PR and depends on this one.

No route or response-shape changes on the SQL backends, so internal/controller/openapi/ and the Postman collections are untouched here.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UCV2vuSwoz7J5VV2hZxcWe

@madhavilosetty-intel
madhavilosetty-intel requested a review from a team as a code owner September 8, 2026 22:37

// SQL leaves this to the profiles_wirelessconfigs foreign key. Mongo has no
// constraints, so look for a referencing row the way RPS did before deleting.
err := r.profileWiFiCol.FindOne(ctx, bson.M{fieldWirelessProfileName: profileName, fieldTenantID: tenantID}).Err()
@codecov

codecov Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.95652% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.74%. Comparing base (c9ec0a5) to head (533580e).

Files with missing lines Patch % Lines
internal/repoerrors/foreignkeyviolation.go 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1260      +/-   ##
==========================================
+ Coverage   56.71%   56.74%   +0.03%     
==========================================
  Files         149      149              
  Lines       12154    12171      +17     
==========================================
+ Hits         6893     6907      +14     
- Misses       5260     5263       +3     
  Partials        1        1              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Two repository behaviors differed between the SQL backends and Mongo,
so the same request returned a different status depending on which
database Console was configured with.

Every Mongo Insert mapped a duplicate-key error to NotUniqueError, but
no Mongo Update did, so a unique-index collision on update surfaced as
a generic DatabaseError and the handler answered 400 instead of 409.
Updating a domain to a suffix another domain already owns is the case
the API tests cover; the mapping is added to all six repositories.

Deleting a wireless profile that an AMT profile still references is
rejected by the profiles_wirelessconfigs foreign key on Postgres and
SQLite. Mongo has no constraints, so the delete succeeded and left the
AMT profile pointing at a wireless profile that no longer exists. The
repository now looks for a referencing document first, which is what
RPS itself did: src/data/postgres/tables/wirelessProfiles.ts queries
profiles_wirelessconfigs before deleting rather than relying on the
constraint.

ForeignKeyViolationError moves from sqldb to repoerrors so both
backends raise one type and the controller's errors.As check works for
either; sqldb keeps the name as an alias, so no other package changes.
Its doc comment said the error had no place in the cross-backend
vocabulary because Mongo lacks constraints - that reasoning is what
left the divergence in place, so the comment is corrected rather than
worked around.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UCV2vuSwoz7J5VV2hZxcWe
@madhavilosetty-intel
madhavilosetty-intel force-pushed the fix/mongo-constraint-parity branch from 8bc04eb to 533580e Compare September 8, 2026 22:49
@sudhir-intc

Copy link
Copy Markdown
Contributor

@madhavilosetty-intel : Few initial comments

  1. do we need statements in the PR description:

🤖 Generated with Claude Code
https://claude.ai/code/session_01UCV2vuSwoz7J5VV2hZxcWe

  1. Can you please point to the github issue this PR is fixing. This seems to fixing a good number of issues when using mongodb.

Copilot AI left a comment

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.

🟡 Changes recommended

The Mongo drivertest responses in wificonfig_test.go hardcode a consoledb.* namespace even though the shared mock helper uses testdb, making the new tests inconsistent and potentially brittle.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR aligns MongoDB repository behavior with the SQL backends so identical API requests return consistent HTTP status codes regardless of the configured storage provider.

Changes:

  • Map MongoDB duplicate-key errors on Update to repoerrors.NotUniqueError (matching SQL behavior and enabling HTTP 409).
  • Add a pre-delete reference check in Mongo WirelessRepo.Delete to emulate SQL’s foreign key constraint behavior (reject deletes that would orphan profile references).
  • Move ForeignKeyViolationError into internal/repoerrors (with an sqldb alias) and add targeted Mongo repo tests for the new behaviors.
File summaries
File Description
internal/usecase/sqldb/foreignkeyviolation.go Re-exports ForeignKeyViolationError as an alias to the shared repoerrors type.
internal/repoerrors/foreignkeyviolation.go Introduces cross-backend ForeignKeyViolationError for consistent controller handling.
internal/usecase/nosqldb/mongo/errors.go Adds a Mongo-side ForeignKeyViolationError instance for wireless delete referential checks.
internal/usecase/nosqldb/mongo/wificonfig.go Adds a reference lookup before delete; maps duplicate key on update to NotUniqueError.
internal/usecase/nosqldb/mongo/wificonfig_test.go Adds tests for duplicate-key-on-update and referential-delete behavior (mocked Mongo wire protocol).
internal/usecase/nosqldb/mongo/profile.go Maps duplicate key on update to NotUniqueError.
internal/usecase/nosqldb/mongo/profile_test.go Adds test coverage for duplicate-key-on-update mapping.
internal/usecase/nosqldb/mongo/ieee8021xconfig.go Maps duplicate key on update to NotUniqueError.
internal/usecase/nosqldb/mongo/ieee8021xconfig_test.go Adds test coverage for duplicate-key-on-update mapping.
internal/usecase/nosqldb/mongo/domain.go Maps duplicate key on update to NotUniqueError.
internal/usecase/nosqldb/mongo/domain_test.go Adds test coverage for duplicate-key-on-update mapping.
internal/usecase/nosqldb/mongo/device.go Maps duplicate key on update to NotUniqueError.
internal/usecase/nosqldb/mongo/device_test.go Adds test coverage for duplicate-key-on-update mapping.
internal/usecase/nosqldb/mongo/ciraconfig.go Maps duplicate key on update to NotUniqueError.
internal/usecase/nosqldb/mongo/ciraconfig_test.go Adds test coverage for duplicate-key-on-update mapping.
Review details

Suppressed comments (1)

internal/usecase/nosqldb/mongo/wificonfig_test.go:258

  • This test hardcodes the mock cursor namespace as "consoledb.profiles_wirelessconfigs", but newMockedDB() returns client.Database("testdb"). Use the same "testdb." namespace used elsewhere in this test suite to keep the drivertest responses consistent.
	md.AddResponses(findResponse("consoledb.profiles_wirelessconfigs",
		bson.D{{Key: "profilename", Value: "amt-profile"}, {Key: "wirelessprofilename", Value: "wifi1"}},
	))
  • Files reviewed: 15/15 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +212 to 214
// No referencing profiles_wirelessconfigs row, then the delete itself.
md.AddResponses(findResponse("consoledb.profiles_wirelessconfigs"), deleteResponse(1))

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.

4 participants