Skip to content

Conversation

@cweidenkeller
Copy link
Contributor

@cweidenkeller cweidenkeller commented Dec 8, 2025

Description

Added schema environments basic crud operations

Motivation and Context

Resolves BED-6851

Why is this change required? What problem does it solve?
This is part of the OG environment expansion.
It allows the app to interact with this new table.

How Has This Been Tested?

Added full suite tests

Screenshots (optional):

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

Summary by CodeRabbit

Release Notes

  • New Features

    • Added schema environment management: create, retrieve, and delete environment mappings.
    • Added database support for relationship findings, remediations, and principal-kind mappings with appropriate constraints and cascades.
  • Migrations

    • Database migration updates introducing new environment-related tables and removing the previous extensions table.
  • Tests

    • Added integration tests covering schema environment CRUD behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 8, 2025

Warning

Rate limit exceeded

@cweidenkeller has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 2 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between faafdf9 and 2081e3b.

📒 Files selected for processing (6)
  • cmd/api/src/database/db.go (3 hunks)
  • cmd/api/src/database/graphschema.go (5 hunks)
  • cmd/api/src/database/graphschema_test.go (1 hunks)
  • cmd/api/src/database/migration/migrations/v8.5.0.sql (2 hunks)
  • cmd/api/src/database/mocks/db.go (3 hunks)
  • cmd/api/src/model/graphschema.go (1 hunks)

Walkthrough

Adds a new SchemaEnvironment entity with model, CRUD DB methods, mocks, integration tests, a raw-query helper (RawFirst), a new public duplicate error, and SQL migrations introducing environment-related tables and relationships.

Changes

Cohort / File(s) Change Summary
Database Layer Utilities
cmd/api/src/database/db.go
Added public error ErrDuplicateSchemaEnvironment and method RawFirst(ctx context.Context, sql string, dest any, values ...any) error; adjusted imports for GORM/Postgres
Database CRUD Implementation
cmd/api/src/database/graphschema.go
Added CreateSchemaEnvironment, GetSchemaEnvironmentById, DeleteSchemaEnvironment; extended OpenGraphSchema interface; duplicate and not-found error handling
Model Definition
cmd/api/src/model/graphschema.go
Added SchemaEnvironment struct and TableName() mapping for schema_environments
Database Schema Migration
cmd/api/src/database/migration/migrations/v8.5.0.sql
Dropped schema_extensions block and added schema_environments, schema_relationship_findings, schema_remediations, schema_environments_principal_kinds with FK constraints, UNIQUEs, and ON DELETE CASCADE rules
Mocks
cmd/api/src/database/mocks/db.go
Added mock methods and recorder entries for CreateSchemaEnvironment, GetSchemaEnvironmentById, and DeleteSchemaEnvironment
Integration Tests
cmd/api/src/database/graphschema_test.go
Added TestDatabase_SchemaEnvironment_CRUD exercising create, duplicate detection, get-by-id, delete, and not-found cases

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to: v8.5.0.sql (FKs, UNIQUE, ON DELETE CASCADE), CreateSchemaEnvironment duplicate-detection logic, RawFirst context and scanning correctness, and test setup/teardown in graphschema_test.go.

Possibly related PRs

Suggested labels

api

Suggested reviewers

  • LawsonWillard
  • wes-mil
  • AD7ZJ

Poem

🐰 I hopped into code with a curious bounce,
New environments planted, rows to pronounce,
Tables and tests in tidy array,
Mocks ready, errors guard the way,
A gentle migration — hop, hop, hooray! 🌿

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding CRUD operations for schema_environments table as part of OpenGraph feature development.
Description check ✅ Passed The description covers key areas but lacks detail in testing specifics and documentation confirmation sections.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cweidenkeller cweidenkeller marked this pull request as draft December 8, 2025 20:54
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/api/src/database/migration/migrations/v8.5.0.sql (1)

59-70: Duplicate table definition for schema_extensions.

The schema_extensions table is defined twice in this migration file (lines 29-40 and lines 60-70). While CREATE TABLE IF NOT EXISTS prevents an error, this duplication is likely unintentional and should be removed.

-- OpenGraph graph schema - extensions (collectors)
-CREATE TABLE IF NOT EXISTS schema_extensions (
-    id SERIAL NOT NULL,
-    name TEXT UNIQUE NOT NULL,
-    display_name TEXT NOT NULL,
-    version TEXT NOT NULL,
-    is_builtin BOOLEAN DEFAULT FALSE,
-    created_at TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp,
-    updated_at TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp,
-    deleted_at TIMESTAMP WITH TIME ZONE DEFAULT NULL,
-    PRIMARY KEY (id)
-);
-
🧹 Nitpick comments (2)
cmd/api/src/model/graphschema.go (1)

87-97: Inconsistent struct pattern: missing Serial embedding and timestamps.

Other schema types (SchemaNodeKind, SchemaEdgeKind, GraphSchemaProperty) embed Serial which provides ID, CreatedAt, UpdatedAt, and DeletedAt fields. SchemaEnvironment defines its own ID field without timestamps.

If this is intentional (simpler entity without audit trail), consider adding a comment explaining the design decision. Otherwise, consider aligning with the existing pattern:

 // SchemaEnvironment - represents an environment mapping for an extension
 type SchemaEnvironment struct {
-	ID                int32 `json:"id" gorm:"primaryKey"`
+	Serial
 	ExtensionId       int32 `json:"extension_id"`
 	EnvironmentKindId int32 `json:"environment_kind_id"`
 	SourceKindId      int32 `json:"source_kind_id"`
 }

Note: This would also require adding timestamp columns to the schema_environments table in the migration.

cmd/api/src/database/migration/migrations/v8.5.0.sql (1)

103-145: Consider adding indexes on foreign key columns.

The new tables have foreign key columns (extension_id, environment_kind_id, source_kind_id, environment_id, finding_id, relationship_kind_id) that may benefit from indexes for query performance, especially if these tables grow large. Other tables in this migration (e.g., schema_node_kinds, schema_properties, schema_edge_kinds) have explicit indexes on their FK columns.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65e7558 and 7f27533.

📒 Files selected for processing (6)
  • cmd/api/src/database/db.go (3 hunks)
  • cmd/api/src/database/graphschema.go (5 hunks)
  • cmd/api/src/database/graphschema_test.go (1 hunks)
  • cmd/api/src/database/migration/migrations/v8.5.0.sql (1 hunks)
  • cmd/api/src/database/mocks/db.go (3 hunks)
  • cmd/api/src/model/graphschema.go (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-25T22:11:53.518Z
Learnt from: LawsonWillard
Repo: SpecterOps/BloodHound PR: 2107
File: cmd/api/src/database/graphschema.go:86-100
Timestamp: 2025-11-25T22:11:53.518Z
Learning: In cmd/api/src/database/graphschema.go, the CreateSchemaEdgeKind method intentionally does not use AuditableTransaction or audit logging because it would create too much noise in the audit log, unlike CreateGraphSchemaExtension which does use auditing.

Applied to files:

  • cmd/api/src/database/graphschema_test.go
  • cmd/api/src/model/graphschema.go
  • cmd/api/src/database/mocks/db.go
  • cmd/api/src/database/graphschema.go
  • cmd/api/src/database/migration/migrations/v8.5.0.sql
📚 Learning: 2025-07-22T20:30:34.839Z
Learnt from: LawsonWillard
Repo: SpecterOps/BloodHound PR: 1700
File: cmd/api/src/api/v2/saved_queries_test.go:3182-3182
Timestamp: 2025-07-22T20:30:34.839Z
Learning: In Go table-driven tests in cmd/api/src/api/v2/saved_queries_test.go, subtest parallelization with t.Parallel() is acceptable when tests are self-contained, each creating their own mock controller (gomock.NewController(t)) and having isolated mock expectations without shared state between subtests.

Applied to files:

  • cmd/api/src/database/graphschema_test.go
📚 Learning: 2025-09-02T16:46:30.895Z
Learnt from: mvlipka
Repo: SpecterOps/BloodHound PR: 1784
File: cmd/api/src/api/v2/auth/auth.go:559-573
Timestamp: 2025-09-02T16:46:30.895Z
Learning: In the BloodHound codebase, when updating user ETAC (Environment Access Control) lists in the UpdateUser function, the approach is to let GORM handle the creation/persistence of the environment access records through model associations rather than using explicit database helper methods like UpdateEnvironmentListForUser.

Applied to files:

  • cmd/api/src/database/graphschema.go
📚 Learning: 2025-06-06T23:12:14.181Z
Learnt from: elikmiller
Repo: SpecterOps/BloodHound PR: 1563
File: packages/go/graphschema/azure/azure.go:24-24
Timestamp: 2025-06-06T23:12:14.181Z
Learning: In BloodHound, files in packages/go/graphschema/*/`*.go` are generated from CUE schemas. When `just prepare-for-codereview` is run, it triggers code generation that may automatically add import aliases or other formatting changes. These changes are legitimate outputs of the generation process, not manual edits that would be overwritten.

Applied to files:

  • cmd/api/src/database/graphschema.go
🧬 Code graph analysis (4)
cmd/api/src/database/graphschema_test.go (2)
cmd/api/src/model/graphschema.go (2)
  • SchemaEnvironment (88-93)
  • SchemaEnvironment (95-97)
cmd/api/src/database/db.go (2)
  • ErrDuplicateSchemaEnvironment (60-60)
  • ErrNotFound (42-42)
cmd/api/src/database/db.go (1)
cmd/api/src/database/helper.go (1)
  • CheckError (26-32)
cmd/api/src/database/mocks/db.go (1)
cmd/api/src/model/graphschema.go (2)
  • SchemaEnvironment (88-93)
  • SchemaEnvironment (95-97)
cmd/api/src/database/graphschema.go (3)
cmd/api/src/model/graphschema.go (2)
  • SchemaEnvironment (88-93)
  • SchemaEnvironment (95-97)
cmd/api/src/database/db.go (3)
  • BloodhoundDB (193-196)
  • ErrDuplicateSchemaEnvironment (60-60)
  • ErrNotFound (42-42)
cmd/api/src/database/helper.go (1)
  • CheckError (26-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: build-ui
  • GitHub Check: run-tests
  • GitHub Check: run-analysis
🔇 Additional comments (12)
cmd/api/src/database/db.go (2)

60-60: LGTM!

New error follows the established pattern for duplicate entity errors in this file.


247-250: The RawFirst method is correctly implemented for its actual usage pattern.

RawFirst is only used for INSERT ... RETURNING statements in the codebase. With INSERT ... RETURNING, the database guarantees a row is returned on success, so the difference between Scan and Take/First is irrelevant. Empty result sets cannot occur with successful INSERT ... RETURNING—the operation either succeeds with a row or fails at the INSERT level. The current implementation using Scan is appropriate and CheckError correctly handles any database-level errors.

Likely an incorrect or invalid review comment.

cmd/api/src/database/graphschema_test.go (2)

435-519: LGTM! Comprehensive CRUD test coverage.

The test follows the established patterns in this file and covers the key scenarios:

  • Successful creation with field validation
  • Duplicate detection for unique constraint
  • Get by ID
  • Delete and subsequent not-found verification
  • Invalid ID handling

The use of unique kind names (TestSchemaEnvEnvironmentKind1, etc.) prevents conflicts with parallel tests.


444-453: The test suite uses pgtestdb.Custom() which provides isolated database instances per test. Each test receives a fresh database with migrations applied, and records inserted in one test do not persist across other tests. Explicit cleanup of kind records is unnecessary.

Likely an incorrect or invalid review comment.

cmd/api/src/database/graphschema.go (4)

46-50: LGTM! Interface properly extended.

The new SchemaEnvironment methods follow the established interface patterns. The absence of an UpdateSchemaEnvironment method appears intentional given the nature of environment mappings (typically immutable once created).


278-294: LGTM! Create implementation follows established patterns.

The implementation correctly:

  • Uses raw SQL with RETURNING for atomic insert-and-fetch
  • Handles duplicate key constraint with appropriate error wrapping
  • Uses CheckError for general error handling

296-302: LGTM! Get implementation is correct.

Uses First() which properly returns gorm.ErrRecordNotFound for missing records, correctly translated to ErrNotFound via CheckError.


304-313: LGTM! Delete implementation follows established patterns.

Correctly checks RowsAffected to return ErrNotFound when the record doesn't exist.

cmd/api/src/database/migration/migrations/v8.5.0.sql (1)

138-145: Verify UNIQUE(principal_kind) constraint intent.

The UNIQUE(principal_kind) constraint means a principal kind can only belong to one environment globally. This seems restrictive—typically you'd want UNIQUE(environment_id, principal_kind) to allow the same principal kind in different environments while preventing duplicates within an environment.

Please confirm this is the intended behavior.

cmd/api/src/database/mocks/db.go (3)

543-556: CreateSchemaEnvironment mock wiring looks correct

Signature, argument order, and return types match surrounding schema methods and model.SchemaEnvironment; gomock call/record patterns are consistent with the rest of the file.


889-901: DeleteSchemaEnvironment mock matches existing delete patterns

Takes ctx and an int32 ID, returns error, and has a corresponding recorder method wired identically to DeleteSchemaEdgeKind / DeleteSchemaNodeKind.


1964-1977: GetSchemaEnvironmentById mock is consistent with other getters

Getter uses ctx + int32 ID and returns model.SchemaEnvironment, error; recorder method mirrors GetSchemaEdgeKindById and GetSchemaNodeKindById patterns.

@cweidenkeller cweidenkeller force-pushed the BED-6851 branch 2 times, most recently from 4cd8c46 to 9ded80d Compare December 8, 2025 21:26
@cweidenkeller cweidenkeller self-assigned this Dec 8, 2025
@cweidenkeller cweidenkeller added the enhancement New feature or request label Dec 8, 2025
@cweidenkeller cweidenkeller marked this pull request as ready for review December 8, 2025 22:57
RETURNING id, schema_extension_id, environment_kind_id, source_kind_id`,
schemaEnvironment.TableName()),
schemaExtensionId, environmentKindId, sourceKindId).Scan(&schemaEnvironment); result.Error != nil {
if strings.Contains(result.Error.Error(), "duplicate key value violates unique constraint") {
Copy link
Contributor

Choose a reason for hiding this comment

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

If you pull main, there's a const you can use defined for this error

const DuplicateKeyValueErrorString = "duplicate key value violates unique constraint"

Everything else looks good.

@kpowderly
Copy link
Contributor

Gonna approve but can you resolve conflicts and run just prepare-for-codereview. You probably need a BHE branch for the mocks as well.

@github-actions github-actions bot locked and limited conversation to collaborators Dec 29, 2025
@cweidenkeller cweidenkeller deleted the BED-6851 branch December 29, 2025 08:48
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants