Skip to content

chore: upsert any custom-node-kinds entries that are missing from kind table BED-7922#2829

Open
AD7ZJ wants to merge 15 commits into
mainfrom
BED-7922--upsert-kind-from-cnk
Open

chore: upsert any custom-node-kinds entries that are missing from kind table BED-7922#2829
AD7ZJ wants to merge 15 commits into
mainfrom
BED-7922--upsert-kind-from-cnk

Conversation

@AD7ZJ

@AD7ZJ AD7ZJ commented May 27, 2026

Copy link
Copy Markdown
Member

Description

This scans through the custom_node_kinds table and upserts to the kind table any kinds that are missing. It also gets rid of the custom_node_kind name column and makes a new kind_id column FK'ed to the kind table.

Motivation and Context

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

How Has This Been Tested?

A migration integration test has been created.

Screenshots (optional):

Types of changes

  • Chore (a change that does not modify the application functionality)
  • Database Migrations

Checklist:

Summary by CodeRabbit

  • Bug Fixes

    • Improved custom node kind behavior so listed and displayed kinds consistently map to stored underlying kinds, avoiding missing/invalid references.
    • Updated custom node kind create/update responses and persistence to use stable kind identifiers.
  • Chores

    • Simplified custom node kind retrieval and validation so the complete custom kind set is used.
    • Updated the custom-node-kind schema/migration flow to upsert and backfill via kind_id, including cleanup of reserved Tag_ kinds.
  • Tests

    • Updated unit/integration tests and mocks to match the new retrieval behavior and kindId response field, including added migration coverage.

@AD7ZJ AD7ZJ self-assigned this May 27, 2026
@AD7ZJ AD7ZJ added the api A pull request containing changes affecting the API code. label May 27, 2026
@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR refactors the custom node kinds data model from using a kind_name string column to a kind_id foreign key reference to the kind table. All database CRUD operations are rewritten to join with the kind table, the schema migration adds and backfills the foreign key constraint, and all API and internal callers are updated to the simplified single-parameter method signature.

Changes

Custom Node Kind kind_id Migration

Layer / File(s) Summary
Data model and interface contract updates
cmd/api/src/model/customnode.go, cmd/api/src/migrations/graph.go, cmd/api/src/database/customnode.go
CustomNodeKind adds a new KindId field and CustomNodeKindConfig.Scan updates its parameter type to any. The GetCustomNodeKinds interface method signature is changed to remove the filters parameter.
Database CRUD implementation refactoring
cmd/api/src/database/customnode.go
CreateCustomNodeKinds upserts kind names to obtain IDs, inserts custom_node_kinds rows with RETURNING id while handling unique violations on kind_id; GetCustomNodeKinds/GetCustomNodeKind run joined selects; UpdateCustomNodeKind/DeleteCustomNodeKind match via joins and return appropriate domain errors.
Database schema migration and function recreation
cmd/api/src/database/migration/migrations/20260526065858_v9_upsert_kind_from_custom_node_kind.sql
Migration deletes reserved Tag_% rows, inserts missing kind rows, adds and backfills nullable kind_id, removes unresolved rows, enforces NOT NULL and FK/unique constraints on kind_id, drops kind_name, and recreates ensure_stubbed_custom_node_kind_for_ingest to use kind_id.
Migration validation integration test
cmd/api/src/database/migration/goose_migration_integration_test.go
New integration test runs migrations, seeds kind/custom_node_kinds, applies the target migration, and asserts schema changes, constraints, correct deletions/survivals, kind upsert behavior, sequence advancement, and non-null kind_id.
API layer and handler test updates
cmd/api/src/api/v2/customnode.go, cmd/api/src/api/v2/customnode_test.go, cmd/api/src/api/v2/kinds.go
API endpoints call GetCustomNodeKinds(ctx) without a filters argument; schemaless kind handling is simplified to directly use returned custom node kinds without prior revalidation. Handler tests update mocked return values and expected JSON responses to include the new kindId field.
Internal service callers and graphschema updates
cmd/api/src/database/graphschema.go, cmd/api/src/database/upsert_schema_extension.go, cmd/api/src/migrations/manifest.go
All internal callers updated to call GetCustomNodeKinds(ctx) without a second argument; schemaless display kind processing is simplified by removing prior name→kind filtering steps. Unused imports are removed.
Mock updates and integration test alignment
cmd/api/src/database/mocks/db.go, cmd/api/src/api/v2/kinds_test.go, cmd/api/src/database/customnode_integration_test.go, cmd/api/src/database/upsert_schema_extension_integration_test.go, cmd/api/src/migrations/migrations_integration_test.go
GoMock signatures updated to the new GetCustomNodeKinds(ctx) shape; tests remove expectations for GetKindsByNames and adapt fixtures. Integration test helpers and queries now join custom_node_kinds to kind on kind_id where appropriate.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • SpecterOps/BloodHound#2766: Changes wiring for SchemalessNodeKindBackfillData.GetCustomNodeKinds and touches the same migration interfaces.
  • SpecterOps/BloodHound#2452: Modifies UpdateCustomNodeKind to change how custom_node_kinds icon-related fields are updated alongside the kind_id schema refactoring.
  • SpecterOps/BloodHound#2839: Relates to the ensure_stubbed_custom_node_kind_for_ingest refactoring to work with custom_node_kinds.kind_id instead of kind_name.

Suggested reviewers

  • sirisjo
  • LawsonWillard
  • mistahj67
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly identifies the main change: upserting custom-node-kinds entries to the kind table, and includes the ticket reference BED-7922.
Description check ✅ Passed The PR description covers all required template sections: description of changes, motivation/context with ticket reference, testing approach, change types, and a completed checklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch BED-7922--upsert-kind-from-cnk

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

@AD7ZJ AD7ZJ marked this pull request as ready for review June 3, 2026 18:04
@coderabbitai coderabbitai Bot added documentation Improvements or additions to documentation enhancement New feature or request go Pull requests that update go code labels Jun 3, 2026

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@cmd/api/src/database/migration/migrations/20260526065858_upsert_kind_from_custom_node_kind.sql`:
- Around line 110-111: The Down migration currently drops
ensure_stubbed_custom_node_kind_for_ingest(TEXT, JSONB) which breaks rollback
paths that still call it; instead, restore/recreate the old kind_name-based
implementation of ensure_stubbed_custom_node_kind_for_ingest in the Down block
(mirror the previous behavior before this migration) rather than dropping it —
implement the function body that accepts (TEXT, JSONB) and uses the old
kind_name logic to look up or stub a kind, matching the signature
ensure_stubbed_custom_node_kind_for_ingest(TEXT, JSONB) so pre-rollback code can
continue to call it.
- Line 16: The migration file
20260526065858_upsert_kind_from_custom_node_kind.sql is missing the required
<version> token in its filename; rename it to follow the repository convention
<timestamp>_<version>_<name>.sql (e.g.,
20260526065858_v9_upsert_kind_from_custom_node_kind.sql or the appropriate next
version like v10) so the goose migration loader recognizes it, and keep the file
contents (including the "-- +goose Up" header) unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 90ad1b39-1b5f-4d89-bb7f-b1e20c94d8a0

📥 Commits

Reviewing files that changed from the base of the PR and between bd004a8 and 6d997e0.

📒 Files selected for processing (12)
  • cmd/api/src/api/v2/customnode.go
  • cmd/api/src/api/v2/kinds.go
  • cmd/api/src/api/v2/kinds_test.go
  • cmd/api/src/database/customnode.go
  • cmd/api/src/database/customnode_integration_test.go
  • cmd/api/src/database/graphschema.go
  • cmd/api/src/database/migration/goose_migration_integration_test.go
  • cmd/api/src/database/migration/migrations/20260526065858_upsert_kind_from_custom_node_kind.sql
  • cmd/api/src/database/mocks/db.go
  • cmd/api/src/database/upsert_schema_extension.go
  • cmd/api/src/database/upsert_schema_extension_integration_test.go
  • cmd/api/src/model/customnode.go

Comment thread cmd/api/src/database/customnode.go Outdated
err := s.AuditableTransaction(ctx, auditEntry, func(tx *gorm.DB) error {
if err := tx.Raw(fmt.Sprintf("DELETE FROM %s WHERE kind_name = ? RETURNING id, config;", model.CustomNodeKind{}.TableName()), kindName).
Row().Scan(&customNodeKind.ID, &customNodeKind.Config); errors.Is(err, sql.ErrNoRows) {
if err := tx.Raw(

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.

Since we are upserting custom node kinds to the kind table would it make sense to delete them from the kinds table here as well? We would need to check that the kind is not part of a current extension for that.

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.

I actually think the opposite is true and this endpoint / handler might need to be deprecated.....

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I marked the handler as deprecated and removed the DeleteCustomNode() logic.

Comment thread cmd/api/src/model/customnode.go Outdated
Comment thread cmd/api/src/model/customnode.go Outdated
Comment thread cmd/api/src/database/customnode.go Outdated
err := s.AuditableTransaction(ctx, auditEntry, func(tx *gorm.DB) error {
if err := tx.Raw(fmt.Sprintf("DELETE FROM %s WHERE kind_name = ? RETURNING id, config;", model.CustomNodeKind{}.TableName()), kindName).
Row().Scan(&customNodeKind.ID, &customNodeKind.Config); errors.Is(err, sql.ErrNoRows) {
if err := tx.Raw(

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.

I actually think the opposite is true and this endpoint / handler might need to be deprecated.....

@urangel urangel 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.

I am unclear whether deprecating this endpoint is a palatable direction to take so will let @mistahj67 follow up on that request. Assuming we do want to go that route, the changes look good to me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api A pull request containing changes affecting the API code. dbmigration documentation Improvements or additions to documentation enhancement New feature or request go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants