Skip to content

Conversation

@AD7ZJ
Copy link
Member

@AD7ZJ AD7ZJ commented Nov 20, 2025

Description

Add a database handler to list extensions with filtering, sorting, and pagination parameters.

Motivation and Context

Resolves https://specterops.atlassian.net/browse/BED-6778

How Has This Been Tested?

A unit test has been added that verifies functionality of the new function.

Screenshots (optional):

Types of changes

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

Checklist:

Summary by CodeRabbit

  • New Features

    • Enhanced graph schema extension querying with advanced filtering, sorting, and pagination support.
  • Tests

    • Added comprehensive integration tests for graph schema extension retrieval with various filter and sort scenarios.
  • Chores

    • Improved internal error handling and SQL query construction utilities.

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

@AD7ZJ AD7ZJ self-assigned this Nov 20, 2025
@AD7ZJ AD7ZJ added enhancement New feature or request api A pull request containing changes affecting the API code. labels Nov 20, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 20, 2025

Walkthrough

This PR introduces a new GetGraphSchemaExtensions method for retrieving and filtering graph schema extensions from the database. The change adds SQL-level filtering, sorting, and pagination capabilities through new helper functions (buildSQLFilter, buildSQLSort) and extends the database interface, implementation, mocks, model types, and integration tests.

Changes

Cohort / File(s) Change Summary
Database Interface & Implementation
cmd/api/src/database/graphschema.go
Added public method GetGraphSchemaExtensions to OpenGraphSchema interface and implemented in BloodhoundDB with SQL-level filtering, sorting (defaulting to id), optional pagination, and total row count calculation.
Database Mocks
cmd/api/src/database/mocks/db.go
Added mock method GetGraphSchemaExtensions and corresponding recorder for testing support.
Model Types
cmd/api/src/model/graphschema.go
Added new exported type GraphSchemaExtensions as a slice of GraphSchemaExtension.
Database Helpers
cmd/api/src/database/helper.go
Added ErrInvalidSortDirection error variable; introduced sqlFilter struct and helper functions buildSQLFilter (constructs PostgreSQL-compatible WHERE clauses with operator and null handling) and buildSQLSort (generates ORDER BY clauses with validation). Modified CheckError to return nil for non-NotFound errors.
Integration Tests
cmd/api/src/database/graphschema_integration_test.go
Added comprehensive TestDatabase_GetGraphSchemaExtensions covering unfiltered queries, single/multiple filters, fuzzy filtering, sorting (ascending/descending), pagination with skip/limit, and error cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas requiring extra attention:
    • buildSQLFilter logic in helper.go: verify PostgreSQL AST construction, operator handling, null semantics, and OR/AND grouping correctness
    • buildSQLSort validation for sort direction and edge cases
    • CheckError behavior change: confirm nil return for non-NotFound errors doesn't break existing callers
    • Integration test coverage: ensure all filter combinations and edge cases are properly validated

Possibly related PRs

Suggested reviewers

  • wes-mil
  • LawsonWillard
  • superlinkx

Poem

🐰 A query builder hops through fields,
Filtering extensions the database yields,
Sorting and paging with SQL delight,
New methods blooming, shining bright!
Tests guard the path, all corners checked—
Graph schemas perfectly intersected! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% 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 a database handler to list/query extension entries, with the ticket reference providing appropriate context.
Description check ✅ Passed The description covers all critical sections: it explains what was added, references the Jira ticket, describes testing approach, identifies the change type, and confirms checklist completion.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BED-6778--OG-db-list-extensions

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca3f2c5 and 88bd82b.

📒 Files selected for processing (3)
  • cmd/api/src/database/graphschema.go (2 hunks)
  • cmd/api/src/database/graphschema_integration_test.go (1 hunks)
  • cmd/api/src/database/mocks/db.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/api/src/database/graphschema_integration_test.go
🧰 Additional context used
🧠 Learnings (3)
📚 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
📚 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.go
  • cmd/api/src/database/mocks/db.go
📚 Learning: 2025-07-10T14:33:20.317Z
Learnt from: JonasBK
Repo: SpecterOps/BloodHound PR: 1671
File: cmd/api/src/analysis/ad/adcs_integration_test.go:3687-3687
Timestamp: 2025-07-10T14:33:20.317Z
Learning: When reviewing Go code, functions defined in one file within a package are accessible from other files in the same package. Before flagging missing functions as compilation errors, check if they exist in other files within the same package directory.

Applied to files:

  • cmd/api/src/database/graphschema.go
🧬 Code graph analysis (2)
cmd/api/src/database/graphschema.go (3)
cmd/api/src/model/filter.go (4)
  • Filters (485-485)
  • Sort (507-507)
  • SortItem (502-505)
  • AscendingSortDirection (491-491)
cmd/api/src/model/graphschema.go (3)
  • GraphSchemaExtensions (19-19)
  • GraphSchemaExtension (21-28)
  • GraphSchemaExtension (30-32)
cmd/api/src/database/helper.go (1)
  • CheckError (39-45)
cmd/api/src/database/mocks/db.go (3)
cmd/api/src/auth/model.go (1)
  • Context (174-178)
cmd/api/src/model/filter.go (2)
  • Filters (485-485)
  • Sort (507-507)
cmd/api/src/model/graphschema.go (1)
  • GraphSchemaExtensions (19-19)
⏰ 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 (3)
cmd/api/src/database/mocks/db.go (1)

1649-1663: LGTM! Generated mock matches the interface.

The generated mock method and recorder correctly match the GetGraphSchemaExtensions interface signature defined in cmd/api/src/database/graphschema.go:30.

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

30-30: LGTM! Interface method addition is well-defined.

The new GetGraphSchemaExtensions method signature appropriately uses model.Filters and model.Sort types for filtering and sorting, with pagination parameters skip and limit, and returns both the results and total count for proper pagination support.


101-163: LGTM! Implementation handles filtering, sorting, and pagination correctly.

The implementation properly:

  • Defaults to sorting by id when no sort is specified (lines 118-120), ensuring consistent pagination
  • Conditionally builds WHERE, ORDER BY, and LIMIT/OFFSET clauses
  • Executes a separate COUNT query only when pagination is requested (lines 149-156)
  • Uses parameterized queries for filter values via filter.params
  • Returns appropriate error types via CheckError

The COUNT query correctly uses only whereClauseString (line 151), avoiding the SQL duplication issue noted in past review comments.

Note: This implementation relies on buildSQLFilter and buildSQLSort helper functions (lines 112, 121) which are not included in this review. Based on past review comments, those helpers should validate column names and operators against an allowlist to prevent SQL injection. Assuming those validations are properly implemented in cmd/api/src/database/helper.go.


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

Base automatically changed from BED-6777--OG-db-create-read-extensions to main November 21, 2025 19:58
@AD7ZJ AD7ZJ changed the title BED-6778: OG DB - List/Query Extension Entries feat: OG DB - List/Query Extension Entries BED-6778 Dec 3, 2025
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: 0

🧹 Nitpick comments (3)
cmd/api/src/database/graphschema_integration_test.go (3)

80-119: Consider making this integration test parallel for consistency

Other integration tests in this file call t.Parallel() at the top; this one does not. Unless this test must serialize due to shared state, consider adding t.Parallel() for consistency and to keep overall suite runtime down.


120-169: Make pagination assertions independent of implicit DB row ordering

The skip/limit subtests assert specific element positions ("charlie" at index 0 after skip=2, "bob" at index 1 for limit=2) while passing an empty Sort{}. That couples the test to whatever default ordering the query/DB happens to use (often insertion order, but not guaranteed across engines or future refactors).

Consider passing an explicit sort (for example, by name or display_name) in these pagination tests and basing expectations on that ordering. This will make the tests more robust and self-document the intended default behavior.


171-175: Relax brittle assertion on the exact Postgres error string

The bogus-filtering subtest asserts the full error message string including driver/engine formatting and SQLSTATE:

require.Equal(t, "ERROR: column \"nonexitentcolumn\" does not exist (SQLSTATE 42703)", err.Error())

This is quite brittle: minor changes in driver, Postgres version, or localization could break the test even though the behavior is still correct. Also, the column name is misspelled in both the query and expected string.

Prefer a looser check such as require.ErrorContains(t, err, "column \"nonexitentcolumn\" does not exist") or asserting on the SQLSTATE code/type if you surface that, and optionally fix the spelling for readability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 132481e and 17b0068.

📒 Files selected for processing (1)
  • cmd/api/src/database/graphschema_integration_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 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_integration_test.go
📚 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_integration_test.go

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

🧹 Nitpick comments (1)
cmd/api/src/database/graphschema_integration_test.go (1)

80-82: Consider adding t.Parallel() for consistency.

Other tests in this file (TestDatabase_CreateAndGetGraphSchemaExtensions, TestBloodhoundDB_SchemaNodeKind_CRUD, etc.) use t.Parallel(). Adding it here would maintain consistency and enable parallel test execution.

 func TestDatabase_GetGraphSchemaExtensions(t *testing.T) {
+	t.Parallel()
 	suite := setupIntegrationTestSuite(t)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17b0068 and 512b630.

📒 Files selected for processing (3)
  • cmd/api/src/database/graphschema.go (2 hunks)
  • cmd/api/src/database/graphschema_integration_test.go (6 hunks)
  • cmd/api/src/database/mocks/db.go (3 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 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.go
  • cmd/api/src/database/graphschema_integration_test.go
  • cmd/api/src/database/mocks/db.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
  • cmd/api/src/database/graphschema_integration_test.go
  • cmd/api/src/database/mocks/db.go
📚 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_integration_test.go
🧬 Code graph analysis (2)
cmd/api/src/database/graphschema_integration_test.go (2)
cmd/api/src/model/filter.go (4)
  • SQLFilter (98-101)
  • Sort (486-486)
  • AscendingSortDirection (466-466)
  • DescendingSortDirection (467-467)
cmd/api/src/database/db.go (2)
  • ErrDuplicateSchemaNodeKindName (56-56)
  • ErrDuplicateSchemaEdgeKindName (58-58)
cmd/api/src/database/mocks/db.go (3)
cmd/api/src/auth/model.go (1)
  • Context (174-178)
cmd/api/src/model/graphschema.go (5)
  • SchemaNodeKind (45-55)
  • SchemaNodeKind (58-60)
  • GraphSchemaExtensions (19-19)
  • SchemaEdgeKind (77-83)
  • SchemaEdgeKind (85-87)
cmd/api/src/model/filter.go (2)
  • SQLFilter (98-101)
  • Sort (486-486)
🪛 golangci-lint (2.5.0)
cmd/api/src/database/graphschema_integration_test.go

[major] 236-236: : # github.com/specterops/bloodhound/cmd/api/src/database_test [github.com/specterops/bloodhound/cmd/api/src/database.test]
cmd/api/src/database/graphschema_integration_test.go:236:43: testSuite.BHDatabase.GetSchemaNodeKindByID undefined (type *database.BloodhoundDB has no field or method GetSchemaNodeKindByID, but does have method GetSchemaNodeKindById)
cmd/api/src/database/graphschema_integration_test.go:240:32: testSuite.BHDatabase.GetSchemaNodeKindByID undefined (type *database.BloodhoundDB has no field or method GetSchemaNodeKindByID, but does have method GetSchemaNodeKindById)

(typecheck)

⏰ 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: run-analysis
  • GitHub Check: run-tests
  • GitHub Check: build-ui
🔇 Additional comments (3)
cmd/api/src/database/graphschema.go (1)

98-156: Implementation looks good and follows established patterns.

The GetGraphSchemaExtensions method correctly implements:

  • SQL filter application with parameterized queries
  • Default ascending sort by id when no sort is specified
  • Pagination with LIMIT/OFFSET
  • Separate count query for total rows when pagination is used

The use of ToSqlString() for sort direction aligns with the prior review discussion about creating a helper function.

cmd/api/src/database/graphschema_integration_test.go (1)

120-176: Comprehensive test coverage for the new GetGraphSchemaExtensions function.

The test scenarios cover:

  • No filtering/sorting (baseline)
  • Exact match filtering
  • Fuzzy/ILIKE filtering
  • Ascending and descending sort
  • Skip (offset) pagination
  • Limit pagination
  • Error path with invalid column
cmd/api/src/database/mocks/db.go (1)

1635-1649: Generated mock code correctly reflects the new interface method.

The GetGraphSchemaExtensions mock method and its recorder are correctly generated to match the interface signature.

@AD7ZJ AD7ZJ force-pushed the BED-6778--OG-db-list-extensions branch from 512b630 to e3e6204 Compare December 4, 2025 19:13
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: 0

🧹 Nitpick comments (1)
cmd/api/src/database/graphschema.go (1)

98-156: Implementation is sound; consider tightening the doc comment around totalRowCount

The query assembly (filter via SQLFilter, default sort on id ASC, SortDirection.ToSqlString(), and optional LIMIT/OFFSET) looks correct and mirrors existing patterns, and the COUNT(*) for paginated calls correctly returns the total matching rows ignoring pagination.

The only nit is the docstring, which currently reads a bit ambiguously about what the integer represents. Consider clarifying that it’s the total number of rows matching the filter, independent of skip/limit, e.g.:

-// GetGraphSchemaExtensions gets all the rows from the extensions table that match the given SQLFilter. It returns a slice of GraphSchemaExtension structs
-// populated with the data, as well as an integer giving the total number of rows returned by the query (excluding any given pagination)
+// GetGraphSchemaExtensions returns rows from the extensions table that match the given SQLFilter, optionally paginated via skip and limit.
+// It returns the matching extensions and the total number of rows matching the filter (ignoring skip and limit).
 func (s *BloodhoundDB) GetGraphSchemaExtensions(ctx context.Context, extensionSqlFilter model.SQLFilter, sort model.Sort, skip, limit int) (model.GraphSchemaExtensions, int, error) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 512b630 and e3e6204.

📒 Files selected for processing (4)
  • cmd/api/src/database/graphschema.go (2 hunks)
  • cmd/api/src/database/graphschema_integration_test.go (1 hunks)
  • cmd/api/src/database/mocks/db.go (1 hunks)
  • cmd/api/src/model/filter.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • cmd/api/src/model/filter.go
  • cmd/api/src/database/graphschema_integration_test.go
🧰 Additional context used
🧠 Learnings (2)
📚 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.go
  • cmd/api/src/database/mocks/db.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 (1)
cmd/api/src/database/mocks/db.go (2)
cmd/api/src/model/filter.go (2)
  • SQLFilter (98-101)
  • Sort (518-518)
cmd/api/src/model/graphschema.go (1)
  • GraphSchemaExtensions (19-19)
⏰ 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). (5)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-analysis
  • GitHub Check: run-tests
  • GitHub Check: build-ui
  • GitHub Check: run-go-unit-tests
🔇 Additional comments (2)
cmd/api/src/database/graphschema.go (1)

27-46: Interface surface for GetGraphSchemaExtensions looks consistent

The new method on OpenGraphSchema matches the implementation and mock signatures (SQLFilter, Sort, skip/limit, slice + total count + error) and is consistent with other filtered/paginated getters in this interface.

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

1635-1649: New GetGraphSchemaExtensions mock wiring is correct

The mock and recorder for GetGraphSchemaExtensions follow the standard gomock pattern and match the database interface signature, so tests can safely stub and assert the new method.

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: 3

🧹 Nitpick comments (1)
cmd/api/src/database/graphschema_integration_test.go (1)

80-81: Consider adding t.Parallel() for test isolation.

Other integration tests in this file use t.Parallel() (see line 31, 188, 318, 429). While this test doesn't share state with those tests due to unique extension names, adding t.Parallel() would maintain consistency and allow concurrent execution.

Apply this diff:

 func TestDatabase_GetGraphSchemaExtensions(t *testing.T) {
+	t.Parallel()
 	suite := setupIntegrationTestSuite(t)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3e6204 and 3e12ace.

📒 Files selected for processing (2)
  • cmd/api/src/database/graphschema.go (2 hunks)
  • cmd/api/src/database/graphschema_integration_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 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.go
  • cmd/api/src/database/graphschema_integration_test.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
📚 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_integration_test.go
🧬 Code graph analysis (1)
cmd/api/src/database/graphschema.go (4)
cmd/api/src/model/graphschema.go (3)
  • GraphSchemaExtensions (19-19)
  • GraphSchemaExtension (21-28)
  • GraphSchemaExtension (30-32)
packages/go/schemagen/csgen/models.go (1)
  • Operator (64-64)
cmd/api/src/database/db.go (1)
  • BloodhoundDB (191-194)
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: run-tests
  • GitHub Check: build-ui
  • GitHub Check: run-analysis

…r from service layer

This should still be refactored further, but it gives an idea of what we can shoot for.
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: 3

🧹 Nitpick comments (2)
cmd/api/src/database/helper.go (1)

171-194: Add a default case to handle unexpected sort directions defensively.

The switch statement handles AscendingSortDirection, DescendingSortDirection, and InvalidSortDirection, but lacks a default case. If a new SortDirection constant is added in the future, the function would silently produce an empty direction string.

 		switch item.Direction {
 		case model.AscendingSortDirection:
 			dirString = "ASC"
 		case model.DescendingSortDirection:
 			dirString = "DESC"
 		case model.InvalidSortDirection:
 			return "", ErrInvalidSortDirection
+		default:
+			return "", ErrInvalidSortDirection
 		}
cmd/api/src/database/graphschema_integration_test.go (1)

80-83: Missing t.Parallel() call unlike other tests in this file.

Other test functions in this file (TestDatabase_CreateAndGetGraphSchemaExtensions, TestBloodhoundDB_SchemaNodeKind_CRUD, etc.) include t.Parallel() at the start. This test is missing it, which is inconsistent.

If the omission is intentional due to potential test interference, please add a comment explaining why. Otherwise, add t.Parallel() for consistency.

 func TestDatabase_GetGraphSchemaExtensions(t *testing.T) {
+	t.Parallel()
 	suite := setupIntegrationTestSuite(t)
 	defer teardownIntegrationTestSuite(t, &suite)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6bdccc9 and 58fefba.

📒 Files selected for processing (3)
  • cmd/api/src/database/graphschema.go (2 hunks)
  • cmd/api/src/database/graphschema_integration_test.go (1 hunks)
  • cmd/api/src/database/helper.go (2 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_integration_test.go
  • cmd/api/src/database/graphschema.go
📚 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_integration_test.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
📚 Learning: 2025-07-10T14:33:20.317Z
Learnt from: JonasBK
Repo: SpecterOps/BloodHound PR: 1671
File: cmd/api/src/analysis/ad/adcs_integration_test.go:3687-3687
Timestamp: 2025-07-10T14:33:20.317Z
Learning: When reviewing Go code, functions defined in one file within a package are accessible from other files in the same package. Before flagging missing functions as compilation errors, check if they exist in other files within the same package directory.

Applied to files:

  • cmd/api/src/database/graphschema.go
🧬 Code graph analysis (2)
cmd/api/src/database/graphschema.go (4)
cmd/api/src/model/filter.go (4)
  • Filters (485-485)
  • Sort (518-518)
  • SortItem (513-516)
  • AscendingSortDirection (491-491)
cmd/api/src/model/graphschema.go (3)
  • GraphSchemaExtensions (19-19)
  • GraphSchemaExtension (21-28)
  • GraphSchemaExtension (30-32)
cmd/api/src/database/db.go (1)
  • BloodhoundDB (191-194)
cmd/api/src/database/helper.go (1)
  • CheckError (39-45)
cmd/api/src/database/helper.go (2)
cmd/api/src/model/filter.go (12)
  • NullString (57-57)
  • GreaterThan (41-41)
  • GreaterThanOrEquals (42-42)
  • LessThan (43-43)
  • LessThanOrEquals (44-44)
  • NotEquals (46-46)
  • ApproximatelyEquals (47-47)
  • FilterOr (510-510)
  • Sort (518-518)
  • AscendingSortDirection (491-491)
  • DescendingSortDirection (492-492)
  • InvalidSortDirection (490-490)
packages/go/schemagen/csgen/models.go (3)
  • Operator (64-64)
  • OperatorEquals (79-79)
  • SyntaxNode (226-229)
⏰ 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-analysis
  • GitHub Check: run-tests
🔇 Additional comments (7)
cmd/api/src/database/helper.go (2)

32-37: Well-structured additions for SQL building helpers.

The sqlFilter struct and ErrInvalidSortDirection error are appropriately scoped as package-private and public respectively. This aligns with past review feedback to keep helpers private and move them to helper.go.


188-188: Add column name validation to buildSQLSort to prevent SQL injection.

The item.Column value is directly concatenated into the ORDER BY clause without validation. While buildSQLFilter uses safe identifier handling, buildSQLSort concatenates user input directly. Although the API layer (ParseSortParameters) validates columns before calling this function, GetGraphSchemaExtensions can be called directly from other code paths without validation.

Consider validating column names against a whitelist within buildSQLSort:

var allowedColumns = map[string]bool{
    "id": true,
    "name": true,
    "created_at": true,
    // ... other sortable columns
}

if !allowedColumns[item.Column] {
    return "", fmt.Errorf("unsupported sort column: %q", item.Column)
}
sortColumns = append(sortColumns, item.Column+" "+dirString)

This ensures defense-in-depth by validating at the database layer rather than relying solely on upstream API validation.

cmd/api/src/database/graphschema_integration_test.go (1)

120-232: Good test coverage for the new GetGraphSchemaExtensions functionality.

The test cases thoroughly cover filtering (single, multiple, fuzzy), sorting (ascending, descending), and pagination (skip, limit), as well as error handling for invalid columns.

Consider adding test cases for:

  • Invalid sort direction (to verify ErrInvalidSortDirection is returned)
  • OR filter set operator (model.FilterOr)

These would further strengthen the test suite but are not blockers.

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

30-30: Interface signature looks correct.

The new GetGraphSchemaExtensions method signature follows the established patterns in the interface, accepting filters, sort, skip, and limit parameters and returning the result slice, count, and error.


114-121: Good: Default sort ensures consistent pagination.

The default sort by id when no sort is specified is a sensible default that ensures deterministic ordering for pagination. The comment clearly explains the reasoning.


123-129: Pagination implementation is correct.

The LIMIT and OFFSET clauses are properly constructed using %d format specifier for integer values, which is safe from SQL injection.


135-144: Main query construction is well-structured.

The SELECT query properly uses parameterized queries via filter.params for filter values, preventing SQL injection for user-provided values. The query components are cleanly organized.

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: 0

🧹 Nitpick comments (1)
cmd/api/src/database/graphschema_integration_test.go (1)

80-81: Consider adding t.Parallel() for consistency and performance.

The test TestDatabase_GetGraphSchemaExtensions is not marked with t.Parallel(), unlike TestBloodhoundDB_SchemaNodeKind_CRUD at line 235. Based on learnings, integration tests can be parallelized when self-contained. However, if parallel tests create extensions with the same names ("adam", "bob", "charlie", "david"), they may conflict due to unique constraints on extension names.

Consider either:

  1. Adding t.Parallel() and using more unique names (e.g., with random suffixes or test-specific prefixes)
  2. Documenting why parallelization was intentionally omitted

Based on learnings, table-driven tests in BloodHound can be parallelized when using isolated setup.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58fefba and ca3f2c5.

📒 Files selected for processing (3)
  • cmd/api/src/database/graphschema.go (2 hunks)
  • cmd/api/src/database/graphschema_integration_test.go (1 hunks)
  • cmd/api/src/database/mocks/db.go (3 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 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_integration_test.go
  • cmd/api/src/database/graphschema.go
  • cmd/api/src/database/mocks/db.go
📚 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_integration_test.go
📚 Learning: 2025-08-27T21:15:32.207Z
Learnt from: jvacca-specterops
Repo: SpecterOps/BloodHound PR: 1823
File: packages/javascript/bh-shared-ui/src/commonSearchesAGT.ts:184-187
Timestamp: 2025-08-27T21:15:32.207Z
Learning: In the BloodHound codebase, syntax fixes for invalid Cypher patterns (like `*..` → `*1..`) may be considered out of scope for specific PRs, even when flagged during review.

Applied to files:

  • cmd/api/src/database/graphschema_integration_test.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
📚 Learning: 2025-07-10T14:33:20.317Z
Learnt from: JonasBK
Repo: SpecterOps/BloodHound PR: 1671
File: cmd/api/src/analysis/ad/adcs_integration_test.go:3687-3687
Timestamp: 2025-07-10T14:33:20.317Z
Learning: When reviewing Go code, functions defined in one file within a package are accessible from other files in the same package. Before flagging missing functions as compilation errors, check if they exist in other files within the same package directory.

Applied to files:

  • cmd/api/src/database/graphschema.go
🧬 Code graph analysis (2)
cmd/api/src/database/graphschema_integration_test.go (3)
packages/go/graphschema/common/common.go (1)
  • DisplayName (54-54)
cmd/api/src/model/filter.go (5)
  • Filters (485-485)
  • Sort (518-518)
  • ApproximatelyEquals (47-47)
  • AscendingSortDirection (491-491)
  • DescendingSortDirection (492-492)
packages/go/schemagen/csgen/models.go (1)
  • Operator (64-64)
cmd/api/src/database/mocks/db.go (1)
cmd/api/src/model/graphschema.go (5)
  • SchemaNodeKind (45-55)
  • SchemaNodeKind (58-60)
  • GraphSchemaExtensions (19-19)
  • SchemaEdgeKind (77-83)
  • SchemaEdgeKind (85-87)
⏰ 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: run-tests
  • GitHub Check: build-ui
  • GitHub Check: run-analysis
🔇 Additional comments (2)
cmd/api/src/database/graphschema.go (1)

98-160: LGTM!

The implementation of GetGraphSchemaExtensions is well-structured:

  • Proper use of buildSQLFilter and buildSQLSort helpers for SQL safety
  • Default sort by id ensures consistent pagination (lines 115-117)
  • COUNT query correctly avoids duplicate filters (lines 147-149)
  • Appropriate error handling throughout
cmd/api/src/database/mocks/db.go (1)

1635-1649: LGTM!

The new GetGraphSchemaExtensions mock correctly matches the interface signature and follows gomock conventions.

Copy link
Contributor

@wes-mil wes-mil left a comment

Choose a reason for hiding this comment

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

Read through buildSQLFilter like 4 times and still a little confused... I'm just going to trust that it's behaving properly

@AD7ZJ
Copy link
Member Author

AD7ZJ commented Dec 8, 2025

Read through buildSQLFilter like 4 times and still a little confused... I'm just going to trust that it's behaving properly

It is pretty much a copy/paste from BuildSQLFilter() in model/filter.go.
But yeah I agree it's hard to wrap your brain around these!

@AD7ZJ AD7ZJ merged commit 5d6488e into main Dec 9, 2025
11 checks passed
@AD7ZJ AD7ZJ deleted the BED-6778--OG-db-list-extensions branch December 9, 2025 16:33
@github-actions github-actions bot locked and limited conversation to collaborators Dec 9, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

api A pull request containing changes affecting the API code. enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants